What you'll learn
This is the module that makes Compose click. State is any data that can change and should update the UI when it does — and understanding remember and mutableStateOf is the key to every interactive app you'll ever build. We'll end with your first tappable counter.
By the end you'll be able to:
- Explain what state is in Compose
- Create state with
mutableStateOfand keep it withremember - Build an interactive counter
- Preserve state across rotation and understand state hoisting
What state is
Recall the golden rule: UI is a function of state. State is the data that decides what's on screen right now — the text in a field, whether a switch is on, the items in a list, a counter's value. When state changes, Compose recomposes the parts of the UI that read it, redrawing them automatically. You never touch the views yourself; you change the state.
mutableStateOf & remember
Two pieces work together. mutableStateOf(initial) creates a special observable value — when it changes, any composable that read it recomposes. But a composable can re-run many times, and each run would create a fresh state, resetting it. So you wrap it in remember , which preserves the value across recompositions. The idiomatic form uses by so you read and write it like a normal variable:
var count by remember { mutableStateOf(0) }Key idea
mutableStateOf makes it observable; remembermakes it survive recomposition. Forget remember and your value resets to its initial state on every recomposition — a classic beginner bug where "my counter won't go up."An interactive counter
Now the payoff. Reading count in the Text subscribes it to changes; tapping the button runs count++, which updates the state, which recomposes the Textwith the new value. That's the entire loop — state in, event out, recompose:
@Composable
fun Counter() {
// state: a value Compose watches. Changing it triggers recomposition.
var count by remember { mutableStateOf(0) }
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(text = "Count: $count", fontSize = 24.sp)
Button(onClick = { count++ }) { // update state on tap
Text("Increment")
}
}
}Note
count, and because the Text reads it, Compose redraws it for you. This is the declarative model in action — describe the UI for the state, and let recomposition keep it in sync.rememberSaveable
remember survives recomposition, but not bigger events like rotating the phone, which recreates the screen — your count would reset to 0. Swap in rememberSaveable and the value is saved and restored across configuration changes: var count by rememberSaveable { mutableStateOf(0) }. Reach for it whenever losing the state would annoy the user.
State hoisting (a first look)
The Counter above owns its own state, which makes it hard to reuse or test. The Compose pattern is to hoist the state — lift it out of the composable and pass the value down and events up. The composable becomes stateless: it just shows what it's given and reports what happened:
// Stateless: state is passed IN, events are passed OUT.
@Composable
fun Counter(count: Int, onIncrement: () -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Count: $count", fontSize = 24.sp)
Button(onClick = onIncrement) { Text("Increment") }
}
}
// The caller owns the state and passes it down.
@Composable
fun CounterScreen() {
var count by remember { mutableStateOf(0) }
Counter(count = count, onIncrement = { count++ })
}Tip
Recap & quick check
Key takeaways
- State is data that can change and should update the UI — UI is a function of state.
- mutableStateOf creates observable state; reading it subscribes a composable so it recomposes on change.
- remember keeps state across recompositions — without it, the value resets every recomposition.
- rememberSaveable also survives configuration changes like rotation.
- State hoisting — 'state down, events up' — makes composables stateless, reusable, and testable.
Quick check
1. What does mutableStateOf give you?
2. Why wrap state in remember { }?
3. You increment count but the UI never updates. Likely cause?
4. Which keeps state across screen rotation?
5. What does 'state hoisting' mean?
That completes Phase 1 — you can build interactive Compose UIs from scratch! 🎉 In Phase 2 we go deeper: state hoisting, user input, lists, and Material Design 3.