Compose Layouts, Modifiers & Lists

Reviewed & published by Brayan K

Real screens need structure: spacing, alignment, overlap, and scrolling lists. In Compose you achieve all of it with layout composables and modifiers.

Part of the free Kotlin course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

By the end you'll chain Modifiers, arrange children in Box, Column and Row, and render efficient scrolling lists with LazyColumn.

What You'll Learn in This Lesson

1️⃣ Modifiers and Order

A Modifier decorates a composable — size, padding, background, click behavior. You chain calls, and they apply in order from left to right.

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun Banner() {
    // Modifiers chain. They apply in ORDER, left to right.
    Text(
        text = "Welcome",
        modifier = Modifier
            .fillMaxWidth()        // 1) take the full width
            .background(Color.Cyan) // 2) paint the background
            .padding(16.dp)        // 3) inset the text by 16dp
    )
}

Swap two modifiers and the visual result changes — a classic source of "why is my padding the wrong color?" confusion.

2️⃣ Box: Overlapping Children

Box stacks children on top of each other along the z-axis. You position each child with Modifier.align(...).

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun Badge() {
    // Box overlaps children. Align each within the Box.
    Box(modifier = Modifier.size(100.dp)) {
        Text("Background", modifier = Modifier.align(Alignment.TopStart))
        Text("Centered", modifier = Modifier.align(Alignment.Center))
        Text("3", modifier = Modifier.align(Alignment.BottomEnd))
    }
}

For Column and Row, use verticalArrangement/horizontalArrangement for the main axis and the matching alignment for the cross axis.

3️⃣ Efficient Lists with LazyColumn

A plain Column composes every child eagerly. For long lists, LazyColumn composes only what's visible and uses the items() DSL.

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun NameList() {
    val names = listOf("Ada", "Grace", "Linus", "Margaret")

    // LazyColumn only composes visible items — efficient for long lists.
    LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
        items(names) { name ->
            Text(text = name, modifier = Modifier.padding(8.dp))
        }
    }
}

Your turn. Replace the TODO, then run and compare.

// 🎯 YOUR TURN — replace the TODO then run.

import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable

@Composable
fun NumberList() {
    val numbers = listOf(1, 2, 3, 4, 5)

    LazyColumn {
        // 1) Emit one Text per number.
        //    👉 replace TODO with: items(numbers) { n -> Text("Item $n") }
        TODO
    }
}

Mini-Challenge: A Centered Card

Combine a Box, alignment, and a Column to center content on screen.

// 🎯 MINI-CHALLENGE: a centered card.
//
// 1. Write a @Composable named CenteredCard().
// 2. Use a Box with Modifier.fillMaxSize().
// 3. Inside, place a Column aligned to Alignment.Center
//    (use the Box content lambda's align modifier).
// 4. In the Column, show two Text lines: a title and a subtitle.
//
// ✅ Expected: title and subtitle stacked, centered on screen.

@Composable
fun CenteredCard() {
    // your code here
}

Common Errors (and the fix)

Pro Tips

📋 Quick Reference — Layouts

ConceptCompose API
Chain modifiersModifier.padding(8.dp).fillMaxWidth()
Overlap childrenBox { ... }
Spacing (main axis)Arrangement.spacedBy(8.dp)
Cross-axis alignAlignment.CenterVertically
Lazy listLazyColumn { items(list) { } }

🎉 Lesson Complete!

Practice quiz

What is a Modifier used for in Compose?

  • To declare a new composable
  • To change a composable's size, padding, behavior, and appearance
  • To import packages
  • To define state

Answer: To change a composable's size, padding, behavior, and appearance. A Modifier decorates a composable: layout, drawing, padding, click behavior, and more.

How are modifiers combined?

  • By chaining calls: Modifier.padding(8.dp).fillMaxWidth()
  • By a comma-separated list
  • By the '+' operator only
  • They cannot be combined

Answer: By chaining calls: Modifier.padding(8.dp).fillMaxWidth(). Modifiers chain fluently; each call returns a new Modifier.

Why does modifier order matter?

  • It never matters
  • Only for color modifiers
  • Modifiers are applied in sequence, so padding-then-background differs from background-then-padding
  • Alphabetical order is required

Answer: Modifiers are applied in sequence, so padding-then-background differs from background-then-padding. Modifiers apply left to right, so order changes the visual result.

Which composable overlaps its children on top of each other?

  • Column
  • Row
  • Stack
  • Box

Answer: Box. Box stacks children along the z-axis, overlapping them; you align each within it.

Which composable lazily renders a vertically scrolling list?

  • LazyColumn
  • Column
  • ScrollView
  • RecyclerColumn

Answer: LazyColumn. LazyColumn only composes the items currently visible, like RecyclerView.

Inside a LazyColumn, how do you emit many items from a list?

  • for (x in list) Item(x)
  • forEach { Item(it) }
  • items(list) { Item(it) }
  • repeat(list.size)

Answer: items(list) { Item(it) }. The items(list) { } DSL function emits a child for each element lazily.

Which parameter controls spacing along a Column's main (vertical) axis?

  • horizontalAlignment
  • contentScale
  • verticalArrangement
  • padding only

Answer: verticalArrangement. verticalArrangement (e.g. Arrangement.spacedBy) positions children along a Column's main axis.

In a Row, which parameter aligns children on the cross (vertical) axis?

  • verticalAlignment
  • horizontalArrangement
  • verticalArrangement
  • contentAlignment

Answer: verticalAlignment. A Row's cross axis is vertical, so verticalAlignment (e.g. Alignment.CenterVertically) applies.

Which modifier makes a composable as wide as its parent allows?

  • Modifier.wrapContent()
  • Modifier.matchParent()
  • Modifier.fillMaxWidth()
  • Modifier.expand()

Answer: Modifier.fillMaxWidth(). Modifier.fillMaxWidth() stretches the composable to the parent's full width.

Why prefer LazyColumn over a Column for a long list?

  • LazyColumn centers text automatically
  • Column cannot hold more than 10 items
  • LazyColumn requires no imports
  • LazyColumn only composes visible items, so it is efficient for large lists

Answer: LazyColumn only composes visible items, so it is efficient for large lists. A plain Column composes every child eagerly; LazyColumn composes lazily as you scroll.

Continue this course

Frequently asked questions

What is a Modifier, really?

A Modifier is an ordered, immutable chain of decorations you attach to a composable. Each call — padding, background, size, clickable, fillMaxWidth — returns a new Modifier, and they're applied in sequence. Modifiers handle layout, drawing, gestures, and semantics, so most of the 'how does this look and behave' detail lives in the modifier rather than the composable itself.

Why does modifier order matter so much?

Because modifiers apply left to right, each one wrapping the next. Modifier.padding(8.dp).background(Color.Red) pads first, then paints the background only around the inner content, leaving an unpainted margin. Reverse them — background(Color.Red).padding(8.dp) — and the background fills the outer area while the padding pushes content inward. Same modifiers, different visual result.

When do I use Box vs Column vs Row?

Use Column to stack children vertically, Row to lay them out horizontally, and Box to overlap children on the z-axis (for example a badge over an avatar, or text over an image). Box is also handy as a single-child container when you just need to apply alignment or a background. You nest these freely to build complex screens.

How is LazyColumn different from a regular Column?

A Column composes all of its children eagerly, which is fine for a handful of items but wasteful for long or unbounded lists. LazyColumn (and LazyRow) only composes the items currently visible, recycling as you scroll — the Compose equivalent of RecyclerView. You emit children through its DSL, typically items(list) { item -> ... }, rather than a normal loop.