Chapter 4: Building User Interfaces with Jetpack Compose
Building User Interfaces with Jetpack Compose. This is where your app stops being boring text in the console and starts looking like a real, beautiful Android app.
We’re in January 2026, and Jetpack Compose is the official, recommended way to build all new Android UIs (the old XML + View system is legacy now). Compose is declarative (you describe what the UI should look like, not how to draw it step-by-step), written in Kotlin, super fast with live previews in Android Studio, and it handles recomposition automatically when data changes.
Think of Compose like LEGO: each piece is a @Composable function, and you snap them together. No more findViewById headaches!
Let’s build this chapter like we’re coding together on your laptop in Airoli — step by step, with copy-paste examples, explanations, analogies, and a full hands-on project at the end.
1. Composable Functions (The Heart of Compose)
A @Composable function is just a regular Kotlin function… but annotated with @Composable. It can call other composables, and Compose turns them into UI elements on screen.
Key rules:
- Can only be called from inside other @Composable functions (or setContent in Activity).
- They are very fast & lightweight — Compose calls them many times during “recomposition” when state changes.
- No return value usually (they emit UI “side effects”).
Basic example (in your MainActivity.kt):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import androidx.compose.runtime.Composable import androidx.compose.ui.tooling.preview.Preview // For @Preview magic @Composable fun Greeting(name: String) { Text(text = "Hello, $name from Airoli! 🚀") // This is a composable too! } @Preview(showBackground = true) @Composable fun GreetingPreview() { Greeting(name = "Webliance") } |
- @Preview → Android Studio shows a live preview in the right panel (super useful!).
- Run the app → You see the text on screen.
2. Layouts: Row, Column, Box (Arranging Things)
These are the main “containers”:
- Column → Stacks children vertically (like a vertical LinearLayout).
- Row → Places children horizontally (like horizontal LinearLayout).
- Box → Stacks children on top of each other (like FrameLayout — great for overlays, backgrounds).
All take a lambda (content) where you put child composables.
Example — a simple profile card:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding import androidx.compose.ui.unit.dp @Composable fun ProfileCard() { Column(modifier = Modifier.padding(16.dp)) { // Outer vertical stack Text("Webliance", style = MaterialTheme.typography.headlineMedium) Row { // Horizontal row for avatar + info // Imagine an image here later Text("Airoli, Maharashtra") Spacer(Modifier.width(8.dp)) // Gap Text("• Learning Compose") } Box { // Overlay example Text("Background Box", modifier = Modifier.background(Color.LightGray)) Text("Overlay Text", modifier = Modifier.padding(8.dp)) } } } |
- Spacer → Invisible gap (like margin).
- Modifier.padding(16.dp) → Adds space around.
3. Text, Buttons, Images, and Modifiers (The Visible Stuff)
- Text → Displays text. Supports styles, colors, alignment.
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
Text( text = "Welcome to Jetpack Compose!", color = MaterialTheme.colorScheme.primary, fontSize = 24.sp, fontWeight = FontWeight.Bold, textAlign = TextAlign.Center ) |
- Button → Clickable Material button.
|
0 1 2 3 4 5 6 7 8 |
Button(onClick = { println("Clicked! 🎉") }) { Text("Tap Me") } |
- Image → Loads images (from resources, URL, painter).
First, add an image to res/drawable (e.g., download a logo and name it ic_logo.xml or .png).
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
import androidx.compose.ui.res.painterResource Image( painter = painterResource(id = R.drawable.ic_logo), contentDescription = "Webliance Logo", modifier = Modifier.size(100.dp) ) |
- Modifier → The Swiss Army knife! Chainable to customize size, padding, color, click, shape, etc.
Common chains:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
Modifier .fillMaxWidth() // Take full horizontal space .height(56.dp) // Fixed height .padding(horizontal = 16.dp, vertical = 8.dp) .background(Color.Blue, shape = RoundedCornerShape(12.dp)) .clickable { /* action */ } .border(2.dp, Color.Red) |
Analogy: Modifier is like makeup — apply in any order, each changes the “face” of the composable.
4. Material Design Themes and Styling (Make It Pretty & Consistent)
Compose uses Material 3 (Material You) — dynamic colors from wallpaper (on Android 12+), light/dark modes auto.
Wrap your UI in MaterialTheme:
In MainActivity.kt → setContent:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
setContent { MyAppTheme { // We'll define this Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Greeting("Webliance") } } } |
Define theme (create new file Theme.kt):
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@Composable fun MyAppTheme(content: @Composable () -> Unit) { MaterialTheme( colorScheme = darkColorScheme( // or lightColorScheme() primary = Color(0xFF6750A4), // Purple-ish secondary = Color(0xFF625B71), background = Color(0xFFFFFBFE) ), typography = Typography( headlineMedium = TextStyle(fontSize = 28.sp, fontWeight = FontWeight.Bold) ), content = content ) } |
- Auto dark mode: Use isSystemInDarkTheme() to switch.
- Dynamic colors: dynamicDarkColorScheme(context) pulls from wallpaper.
5. Hands-on: Build a Simple UI Screen (Your First Real Compose Screen!)
Goal: A nice “Welcome” screen with title, subtitle, image placeholder, button, and Material theme.
- In your project → Open MainActivity.kt
- Replace the default setContent with this full example:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
package com.webliance.hellowebliance import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.webliance.hellowebliance.ui.theme.HelloWeblianceTheme // Rename if needed class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { HelloWeblianceTheme { // Your theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { WelcomeScreen() } } } } } @Composable fun WelcomeScreen() { Column( modifier = Modifier .fillMaxSize() .padding(24.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { // Logo/Image Image( painter = painterResource(id = R.drawable.ic_launcher_foreground), // Use your own later contentDescription = "App Logo", modifier = Modifier .size(120.dp) .background(Color.LightGray, RoundedCornerShape(16.dp)) .padding(16.dp) ) Spacer(modifier = Modifier.height(32.dp)) // Title Text( text = "Welcome to Webliance App!", fontSize = 28.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.primary, textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(16.dp)) // Subtitle Text( text = "Building beautiful Android apps from Airoli with Jetpack Compose", fontSize = 16.sp, color = MaterialTheme.colorScheme.onBackground, textAlign = TextAlign.Center ) Spacer(modifier = Modifier.height(48.dp)) // Button Button( onClick = { /* Later: navigate or show toast */ }, modifier = Modifier .fillMaxWidth(0.8f) .height(56.dp), shape = RoundedCornerShape(12.dp), colors = ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.primary ) ) { Text("Get Started", fontSize = 18.sp) } } } @Preview(showBackground = true, device = "spec:width=411dp,height=891dp") @Composable fun WelcomeScreenPreview() { HelloWeblianceTheme { WelcomeScreen() } } |
- Fix theme — If you don’t have HelloWeblianceTheme, create ui/theme/Theme.kt (Android Studio can generate it via right-click → New → Compose Theme).
- Run → See your beautiful screen! Resize emulator to see responsiveness.
- Play:
- Change colors in theme.
- Add .clickable { } on Image.
- Use @Preview parameter uiMode = Configuration.UI_MODE_NIGHT_YES to preview dark mode.
You’ve just built your first real Compose UI! No XML, pure Kotlin, live preview — feels magical, right?
Questions? Want to add state (click button → change text)? Image from URL? Or fix any error? Tell me — next chapter we’ll make it interactive with state & ViewModel. You’re progressing fast — super proud! Keep building! 🚀💜
