Building a high-performance user interface (UI) is the cornerstone of any successful Android application. For years, developers relied on XML layouts, but the industry has shifted. Enter Jetpack Compose—Android’s modern, declarative toolkit for building native UI.
Whether you are a beginner or a seasoned developer migrating from XML, this comprehensive Jetpack Compose tutorial will take you from the basic building blocks to creating a professional Material 3 card-based interface.
What is Jetpack Compose?
Jetpack Compose is a declarative UI toolkit. In the “old” imperative way (XML), you had to manually manage the state of the UI (e.g., findViewById and then calling setText).
In a declarative approach, you simply describe what the UI should look like for a given state. When the data changes, Compose automatically updates the UI. This process is known as Recomposition.
Jetpack Compose vs. Android XML: At a Glance
| Feature | Android XML (Imperative) | Jetpack Compose (Declarative) |
|---|---|---|
| Language | XML + Java/Kotlin | 100% Kotlin |
| UI Updates | Manual (setText, setVisibility) | Automatic (State-driven) |
| Code Volume | High (Separate layout and logic) | Low (UI and logic in one place) |
| Preview | Static Preview | Interactive/Live Previews |
Understanding Composable Functions
The heart of Jetpack Compose is the @Composable function. These are special Kotlin functions that define a piece of your UI.
To create one, simply annotate your function with @Composable. These functions are the “building blocks” of your app; you can nest them within each other to create complex hierarchies.
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
fun GreetingText(name: String) {
// The Text component is a built-in Composable
Text(text = "Hello $name! Welcome to Jetpack Compose.")
}
Code Breakdown:
@Composable: Tells the Kotlin compiler that this function is intended to convert data into UI.Text(): A predefined Composable from the Material 3 library used to display text on the screen.
Mastering Jetpack Compose Layouts
In XML, you used LinearLayout or ConstraintLayout. In Compose, you use a few powerful layout components to arrange your elements.
1. Column (Vertical Stacking)
The Column layout stacks elements on top of each other.
@Composable
fun VerticalLayoutExample() {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("Item 1")
Text("Item 2")
Text("Item 3")
}
}
2. Row (Horizontal Stacking)
The Row layout places elements side-by-side.
@Composable
fun HorizontalLayoutExample() {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text("Left Side")
Text("Right Side")
}
}
3. Box (Overlapping Elements)
The Box layout allows you to stack elements on top of each other (Z-axis).
@Composable
fun OverlayExample() {
Box(contentAlignment = Alignment.Center) {
Image(painter = painterResource(id = R.drawable.bg_image), contentDescription = "Background")
Text("Text Over Image", color = Color.White)
}
}
4. Lazy Layouts (Scrollable Content)
For lists with hundreds of items, Column is inefficient because it renders everything at once. Instead, use Lazy Layouts, which only render items currently visible on the screen.
@Composable
fun FruitListExample() {
val fruits = listOf("Apple", "Banana", "Cherry", "Date", "Elderberry")
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(fruits) { fruit ->
Text(
text = "Fruit: $fruit",
modifier = Modifier.padding(16.dp)
)
}
}
}
Essential UI Components in Material Design 3
By mastering these three core components, you can build almost any interface.
The Text Component
Text(
text = "Hello World",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.Blue
)
The Image Component
Image(
painter = painterResource(id = R.drawable.my_android_logo),
contentDescription = "Android Logo", // Essential for accessibility
modifier = Modifier.size(64.dp)
)
The Button Component
Button(onClick = { println("Button Clicked!") }) {
Text(text = "Click Me")
}
Pro Tip: The Power of Modifiers
If Composable functions are the “what,” Modifiers are the “how.” Modifiers allow you to adjust the appearance and behavior of a component.
Text(
text = "Styled Text",
modifier = Modifier
.padding(16.dp) // Adds space around the element
.background(Color.LightGray) // Sets background color
.fillMaxWidth() // Makes element take full screen width
.clickable { /* Handle click */ } // Makes element interactive
)
Project: Building a Material 3 Card List
Let’s combine everything into a professional, scrollable list of cards.
1. The Data Model
First, define a data class to hold your card information.
data class CardItem(
val title: String,
val summary: String,
val imageRes: Int
)
2. The Individual Card UI
We will nest a Row (for image and text) and a Column (for title and summary) inside a Material 3 Card.
@Composable
fun ListItem(item: CardItem) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Row(modifier = Modifier.padding(16.dp)) {
Image(
painter = painterResource(id = item.imageRes),
contentDescription = null,
modifier = Modifier.size(60.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column {
Text(text = item.title, fontWeight = FontWeight.Bold, fontSize = 18.sp)
Text(text = item.summary, fontSize = 14.sp)
Button(onClick = { /* Action here */ }) {
Text("Read More")
}
}
}
}
}
3. The Main Screen (Scaffold & Surface)
Finally, we wrap everything in a Scaffold to ensure the UI doesn’t overlap with the system status bar.
@Composable
fun MainScreen() {
val sampleData = listOf(
CardItem("Jetpack Compose", "The modern way to build UI", R.drawable.compose_img),
CardItem("Kotlin Multiplatform", "Share code across iOS and Android", R.drawable.kmp_img),
CardItem("Material 3", "Latest design system by Google", R.drawable.m3_img)
)
Scaffold(
topBar = {
Text(
text = "My Learning App",
modifier = Modifier.padding(16.dp),
style = MaterialTheme.typography.headlineMedium
)
}
) { paddingValues ->
Surface(modifier = Modifier.padding(paddingValues)) {
LazyColumn {
items(sampleData) { item ->
ListItem(item)
}
}
}
}
}
FAQ: Common Jetpack Compose Questions
Q: Is Jetpack Compose replacing XML entirely? A: For new projects, yes. Google recommends Compose for all new Android development. However, Compose is interoperable, meaning you can use Compose views inside XML layouts and vice versa.
Q: What is the difference between Column and LazyColumn?
A: A Column renders all its children at once. A LazyColumn only renders the items currently visible on the screen, making it essential for performance in long lists.
Q: What is a “Recomposition” in Compose? A: Recomposition is the process where Compose calls your Composable functions again with new data to update the UI. It only updates the parts of the UI that actually changed.
Q: How do I handle clicks in Jetpack Compose?
A: You can use the onClick parameter in a Button or add a .clickable { } modifier to any other component like a Box or Text.
📌 Full Course Playlist https://www.youtube.com/playlist?list=PLO1OrQEU0vHNmD9Xqzs-qXwzzwrDvdhVu
~ ~ THANK YOU FOR READING ~ ~