Intermediate: Android basics · Jetpack Compose UI · topic 23 of 50 · 35 min
State and recomposition
Explanation
What is state?
State is data that changes and that the UI shows: the number of items in a cart, the text in a search box, whether a switch is on. In declarative UI, you do not change the screen directly; you change the state, and the UI updates itself.
Why a plain var does not work
@Composable
fun BrokenCounter() {
var count = 0
Button(onClick = { count++ }) {
Text("Clicked $count times") // always shows 0
}
}mutableStateOf and remember
@Composable
fun Counter() {
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("Clicked $count times")
}
}- mutableStateOf(0) creates an observable state holder. When its value changes, Compose knows which composables read it.
- remember { } keeps the object across recompositions, so the function running again does not reset it.
- by is a Kotlin property delegate: it lets you write count instead of count.value.
Recomposition
Recomposition is Compose calling your composable functions again with new state to update the UI. Compose is smart: it only re-runs the composables that read the changed state, and skips the rest. Because composables may run often and in any order, they should be fast and free of side effects: do not start network calls or change other variables directly inside a composable body.
rememberSaveable
remember survives recomposition but not configuration changes like rotation, because the activity is recreated. rememberSaveable saves the value in the saved-instance state too, so it survives rotation and even process death. Use it for small UI state like text in a field or the selected tab. Larger or business state belongs in a ViewModel (module 7).
State hoisting
State hoisting means moving state out of a composable to its caller. The composable receives the current value and a function to call when it should change. It becomes stateless: easier to reuse, preview and test, and the screen has a single source of truth.
@Composable
fun QuantityPicker(qty: Int, onQtyChange: (Int) -> Unit) { // stateless
Row(verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = { if (qty > 1) onQtyChange(qty - 1) }) { Text("-") }
Text("$qty")
IconButton(onClick = { onQtyChange(qty + 1) }) { Text("+") }
}
}
@Composable
fun CartLine() {
var qty by rememberSaveable { mutableStateOf(1) } // state lives here
QuantityPicker(qty = qty, onQtyChange = { qty = it })
Text("Total: Rs ${qty * 120}")
}Common mistakes
- Using a plain var or a normal list for UI state: the screen never updates.
- Forgetting remember: the state resets on every recomposition.
- Using remember for state that must survive rotation: use rememberSaveable or a ViewModel.
- Changing a mutable list inside state (list.add) without creating a new list. Use mutableStateListOf or replace the list: items = items + newItem.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
State and recomposition · 1/7
State is data that changes and is shown on screen. Change the state, and the UI updates.
Prefer a video by a teacher? Find free videos on “jetpack compose state remember mutableStateOf state hoisting” on YouTube ↗
In real life: A tip selector in a food delivery checkout
At checkout, a food delivery app lets users add a tip of Rs 0, 10, 20 or 50 for the rider. The selected tip is hoisted state in the checkout screen; the TipSelector composable just shows buttons and reports clicks. The total updates instantly when a tip is tapped, and the choice survives rotating the phone because it uses rememberSaveable.
@Composable
fun TipSelector(selected: Int, onSelect: (Int) -> Unit) {
Row {
listOf(0, 10, 20, 50).forEach { tip ->
FilterChip(selected = tip == selected, onClick = { onSelect(tip) }, label = { Text("Rs $tip") })
}
}
}
@Composable
fun Checkout(foodTotal: Int) {
var tip by rememberSaveable { mutableStateOf(0) }
Column {
TipSelector(selected = tip, onSelect = { tip = it })
Text("To pay: Rs ${foodTotal + tip}")
}
}Practice (0/3 done)
Why does Text("$count") never change in a composable that uses var count = 0 and count++ on click?
What is state hoisting?
Build a stateless LikeButton(liked: Boolean, count: Int, onToggle: () -> Unit) and a parent PostCard that holds liked and count with rememberSaveable. Tapping toggles liked and adds or subtracts 1. Rotate the phone to check the state survives.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Layouts: Column, Row, Box and ScaffoldJetpack Compose UI · same module
- Lists and gridsJetpack Compose UI · same module
- Interview questions: Android and ComposeAndroid career path
- ViewModel and MVVMNavigation and app architecture
- Debugging and performanceTesting Android apps
- Scope, hoisting and closuresFrontend Web Development · JavaScript in depth
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does remember do?
Answer: Keeps a value across recompositions
Without it, the value is recreated each time.
What is recomposition?
Answer: Compose re-running composables that read changed state
Only affected composables re-run.
Which survives screen rotation?
Answer: rememberSaveable
rememberSaveable stores the value in saved state.
Why should composables avoid side effects like network calls in their body?
Answer: They may run many times and in any order
Recomposition can happen often.
What survives process death for small UI values?Related
Answer: rememberSaveable
Saved instance state is restored.
Which function gets a ViewModel inside a composable (without Hilt)?Related
Which survives a configuration change?Related
Can you call a @Composable function inside a click handler?Related
Answer: No; change state and let the UI recompose
Composables can only be called from other composables.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 3 more topics in “Jetpack Compose UI” before its exam: Layouts: Column, Row, Box and Scaffold, Lists and grids, Text input and forms.
- Module 6 exam: Jetpack Compose UI · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Navigation and app architecture. 50 topics left in the course before the final exam.