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
LaunchedEffector an event handler. - No reading mutable non-state. A plain
varat class level will not trigger anything — usemutableStateOf. - 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.
