Learn Jetpack Compose - Layouting & UI Building Blocks
Episode 4 of 23

Learn Jetpack Compose - Layouting & UI Building Blocks

This episode masters Compose's basic layout blocks: Column, Row, Box, LazyColumn, and LazyRow, Material 3 components like Button, TextField, and Card, the Modifier system for spacing and alignment, and responsive layout strategies.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Your project is up and your first composable is running. Now it's time to build real screens. Episode 4 equips you with Compose's layout building blocks: Column, Row, Box, and the Lazy collections, plus Material 3 components.

The key to fluency here is understanding Modifier. Almost every visual aspect — spacing, size, position, clicks — is handled through modifiers, not through XML attributes like before. Once you master Modifier, reading other people's layouts becomes easy.

Episode 4 covers core layout composables, Material components, the Modifier system, and responsive layout.

Core Layout Composable

Column, Row, and Box

These three basic layouts make up 90 percent of your screens: Column stacks children vertically, Row arranges them horizontally, and Box stacks elements on top of one another.

KotlinColumn, Row, Box
@Composable
fun ProfilSingkat() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Arman Dwi", style = MaterialTheme.typography.titleLarge)
        Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
            Button(onClick = { }) { Text("Ikuti") }
            OutlinedButton(onClick = { }) { Text("Pesan") }
        }
        Box(
            modifier = Modifier
                .size(64.dp)
                .background(MaterialTheme.colorScheme.primary)
        ) {
            Text("logo", Modifier.align(Alignment.Center))
        }
    }
}

Arrangement.spacedBy(8.dp) gives consistent spacing between children, and Alignment positions them. Inside Box, Modifier.align(Alignment.Center) centers the text in the box — a common pattern for badges and overlays.

LazyColumn and LazyRow

For long lists, use LazyColumn (vertical) and LazyRow (horizontal). Both only render the items that are visible — an efficiency a regular Column doesn't have. The details of lazy lists are covered in depth in episode 9.

KotlinSimple LazyColumn
LazyColumn {
    items(20) { index ->
        ListItem(
            headlineContent = { Text("Item ke-$index") }
        )
    }
}

items(20) creates items lazily based on the index. As your list grows larger, you'll use items with data lists and key (episodes 9 and 15).

Material Components

Button, Text, and TextField

Material 3 provides variants of Button, Text, and text input that directly use your theme:

KotlinMaterial 3 components
Button(onClick = { onKirim() }) {
    Text("Kirim")
}
 
OutlinedTextField(
    value = pesan,
    onValueChange = { pesan = it },
    label = { Text("Pesan") },
    modifier = Modifier.fillMaxWidth()
)

OutlinedTextField is the standard Material 3 text input with a built-in label. onValueChange sends changes for every character — a state pattern that will be unpacked in episodes 5 and 7.

Card and Scaffold

Card wraps content in a raised surface, while Scaffold provides a screen skeleton with a top bar, content, and a floating action button:

KotlinScaffold and Card
Scaffold(
    topBar = { TopAppBar(title = { Text("Beranda") }) },
    floatingActionButton = {
        FloatingActionButton(onClick = { }) {
            Text("+")
        }
    }
) { padding ->
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(padding)
            .padding(16.dp)
    ) {
        Text("Konten utama", Modifier.padding(16.dp))
    }
}

Scaffold passes padding through its content lambda — you must apply this padding to the content so it isn't hidden behind the top bar. The slot API pattern you saw in episode 2 is at play here.

The Modifier System

Modifier Chains

Modifiers are chained with dot notation. Order matters: the first modifier is the outermost one. The example below sets size, then padding, then background — the final background follows the area after padding:

KotlinModifier chain
Modifier
    .fillMaxWidth()
    .padding(16.dp)
    .background(Color.LightGray)
    .clip(RoundedCornerShape(8.dp))

clip constrains the shape of the following content, so placing it after background clips the corners. Modifiers like .weight inside a Row or Column make elements fill the remaining space — the basis of responsive layout.

Responsive Layout

BoxWithConstraints

Android screen sizes vary. BoxWithConstraints gives you the available constraints so the layout can adapt:

KotlinResponsive layout
@Composable
fun DaftarBerita() {
    BoxWithConstraints {
        val isLebar = maxWidth > 600.dp
        if (isLebar) {
            Row {
                HeaderBerita(Modifier.weight(1f))
                DetailBerita(Modifier.weight(1f))
            }
        } else {
            Column {
                HeaderBerita(Modifier.fillMaxWidth())
                DetailBerita(Modifier.fillMaxWidth())
            }
        }
    }
}

maxWidth > 600.dp detects wide screens (tablets or landscape), then switches from a Column to a Row with weight. This is a simple pattern to start building adaptive UI. To find out the screen size of the active emulator, run adb shell wm size and adb shell wm density in the terminal.

Closing

Episode 4 equipped you with layout building blocks: Column, Row, Box for basic arrangement, LazyColumn and LazyRow for lists, Material 3 components like Button, TextField, Card, and Scaffold, the Modifier system for visual control, and BoxWithConstraints for responsive layout.

Key takeaways:

  • Column is vertical, Row is horizontal, Box stacks elements.
  • LazyColumn and LazyRow render items only when visible.
  • Scaffold provides a screen skeleton with a padding slot.
  • Modifiers chain in an order that determines the final result.
  • weight inside Row and Column makes elements fill space.
  • BoxWithConstraints enables adaptation to wide and narrow screens.

In episode 5 we will discuss state and recomposition — state hoisting, remember, mutableStateOf, ViewModel integration, snapshotFlow and derivedStateOf, and strategies for managing state so the UI always stays in sync with data.

Learn Jetpack Compose - Layouting & UI Building Blocks | Learn Jetpack Compose