Getting Started With Android UI Development With Jetpack Compose
Learn how to build a simple app using Jetpack Compose. Jetpack Compose is a reactive programming pattern that powers Android UI development.
Join the DZone community and get the full member experience.
Join For FreeI was contacted by my friend Aditya last month with tremendous enthusiasm regarding a new Android feature he had discovered. "Mohit, you won't believe it! I just used Jetpack Compose and it's insane!" At first, I was like, "Whatever dude, another Google framework that will be obsolete next year." But then, Dan showed me his project, and frankly, I was kind of blown away.
Why We Started Working With Jetpack Compose
Aditya had been struggling with a dating app he was building using XML layouts for roughly six months. The UI would always get broken on different devices, and animations were a nightmare. Within two weeks of using Compose, he rewrote the entire thing, and it worked way better.
What persuaded us:
- Coding required way less code (like, for real, 30-40% less).
- Changes show up while you type (no longer waiting forever for Gradle).
- Animations are now finally making sense.
- Apps are more responsive on our elderly test phones.
- When we are stuck, there are hundreds of individuals online to assist.
Constructing Our First Project Together
When Aditya and I decided to work together, the start was very smooth.
Getting Our Tools Ready
- We downloaded Android Studio.
- Ensured we had lots of coffee and snacks.
- Created a new project with the "Empty Compose Activity" option.
This is what we had in our build files (after messing with it three times to start with).
// In your project's build.gradle
ext {
compose_version = '1.5.4'
}
// In your app's build.gradle
android {
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion compose_version
}
}
dependencies {
implementation platform('androidx.compose:compose-bom:2023.10.01')
implementation 'androidx.compose.material3:material3'
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-tooling-preview'
}
Our First Screen
Aditya was insisting on starting with something complex, but I convinced him to keep it simple — a welcome card. Here's what we did:
@Composable
fun WelcomeCard(name: String) {
Card(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
) {
Column(
modifier = Modifier.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Welcome, $name!",
style = MaterialTheme.typography.headlineMedium
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Ready to build something awesome?",
style = MaterialTheme.typography.bodyLarge
)
}
}
}
It took us about 10 minutes to write, and it was really good!
Making Sense of the Building Blocks
When I tried to convey this to our new Android buddy, here's how I phrased it:
Composable: That only informs Android, 'Hey, this function creates UI things!'Modifier: Sort of like Android's CSS, it makes stuff show up and act- Material components: Google's ready-to-use UI material that is beautiful with minimal effort
Adding Some Interaction
Static screens are just so booooring, so we added a simple card that opens up when you click it:
@Composable
fun InteractiveCard() {
var isExpanded by remember { mutableStateOf(false) }
Card(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth()
.clickable { isExpanded = !isExpanded }
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Click me to see more!",
style = MaterialTheme.typography.titleMedium
)
if (isExpanded) {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Hey, you found the hidden content! ",
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
The Magic of State
The first time we encountered remember {mutableStateOf(false)}, it seemed quite challenging, but with time, it actually feels easier.
rememberis used to save data with respect to the screen being refreshed.mutableStateOfimplies a state that can be changed.- When the state revalidates, only those parts of the UI that are relevant will get altered.
Layouts That Actually Make Sense
The biggest moment was when we saw how layouts are done. XML layouts were a nightmare of deep hierarchies of ViewGroups, but Compose removes all of that.
Row: Fill Side-by-Side
We created this profile header with Row. Flutter widgets basically work the same way with Row: fill side-by-side.
@Composable
fun ProfileHeader(name: String, title: String) {
Row(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
// Profile pic (just a gray circle for now)
Box(
modifier = Modifier
.size(48.dp)
.background(Color.Gray, CircleShape)
)
// Text details
Column {
Text(
text = name,
style = MaterialTheme.typography.titleMedium
)
Text(
text = title,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
Column: Stuff That Should Be Stacked Vertically
For this part of the messaging application, we utilized the Column implementation.
@Composable
fun MessageCard(message: String, time: String, isUnread: Boolean) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.background(
color = if (isUnread)
MaterialTheme.colorScheme.primaryContainer
else MaterialTheme.colorScheme.surface,
shape = RoundedCornerShape(8.dp)
)
.padding(16.dp)
) {
// Message content
Text(
text = message,
style = MaterialTheme.typography.bodyLarge
)
Spacer(modifier = Modifier.height(8.dp))
// Time and status
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = time,
style = MaterialTheme.typography.bodySmall
)
if (isUnread) {
Text(
text = "NEW",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary
)
}
}
}
}
Things We Did Wrong (So You Don't Have To)
We committed several errors on the way. Here are some of the things for you to consider:
1. Modifier Order Matters a Lot
// Wrong (padding gets applied to full width)
Modifier.padding(16.dp).fillMaxWidth()
// Right (padding applies after width)
Modifier.fillMaxWidth().padding(16.dp)
2. State Management
// Wrong (resets every time the UI updates)
var count = 0
// Right (survives UI updates)
var count by remember { mutableStateOf(0) }
What We Knew
Once Aditya and I had developed our first application using Compose, we wanted to know the following first:
- Begin small: Don't try to change your whole app at once. Change one screen at a time.
- Use preview: The
Previewthing is actually very handy — use it to preview your changes without having to run the app. - Think in pieces: Break your UI into smaller, reusable pieces.
Aditya also sometimes plays around with nested modifiers (I'm always having to correct him lol), but we both improve daily.
If you are new to all this, don't worry so much about making everything perfect. Just play around with it and have fun! Good luck!
Opinions expressed by DZone contributors are their own.
Comments