← Jetpack Compose from Zero to Production

Thinking in composables

Lesson 1 of 51 min read

Why UI is a function of state, and what recomposition really does.

In XML you created views and then mutated them: textView.text = "Hello". In Compose you describe what the UI should look like for the current state, and Compose works out the difference.

@Composable
fun Greeting(name: String) {
    Text("Hello $name")
}

Call it with a new name and the text changes. You never touch a Text object.

Recomposition in one paragraph

Compose records which composables read which state. When that state changes, it re-runs only those composables. It is not a full-screen redraw, and an unchanged composable with stable parameters is skipped entirely.

The three rules this creates

  • No side effects in a composable body. It can run many times, in any order, and be cancelled halfway. Network calls, analytics and navigation go in LaunchedEffect or an event handler.
  • No reading mutable non-state. A plain var at class level will not trigger anything — use mutableStateOf.
  • Composables return nothing. They emit UI. If you want a value, compute it before the call.

Modifiers are ordered

// padding then background: the padding is outside the colour
Modifier.padding(16.dp).background(Color.Red)

// background then padding: the colour fills the padded area
Modifier.background(Color.Red).padding(16.dp)

Reading modifier chains left to right as a pipeline removes about half of all "why does it look like that" moments.

The usual first mistake: calling a suspend function or starting a request directly in a composable body. It will fire again on every recomposition. Wrap it in LaunchedEffect(key) and it runs once per key.