Skip to content

Intermediate: Android basics · Jetpack Compose UI · topic 23 of 50 · 35 min

State and recomposition

Explanation

State is any value that can change over time and should be shown on screen. In Compose, store it in a State object, usually with remember { mutableStateOf(...) }. When the value changes, Compose re-runs (recomposes) the composables that read it. A plain var does not trigger updates. rememberSaveable also survives rotation. State hoisting moves state up to the caller, giving reusable, stateless composables.

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

kotlin
@Composable
fun BrokenCounter() {
    var count = 0
    Button(onClick = { count++ }) {
        Text("Clicked $count times")      // always shows 0
    }
}
Compose does not know count changed, and even if it redraws, the function runs again and count is reset to 0.

mutableStateOf and remember

kotlin
@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }
    Button(onClick = { count++ }) {
        Text("Clicked $count times")
    }
}
Now each click updates the text.
  • 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.

Think of it like this: State is like the score on a cricket scoreboard. The operator changes only the number; the board shows the new number automatically. A plain var is like writing the score on a piece of paper in your pocket: the crowd never sees it change.

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.

kotlin
@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}")
}
State goes down as parameters; events go up as lambdas. This is unidirectional data flow.

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 → UI

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.

kotlin
@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}")
    }
}
One source of truth for the tip, shown in two places.

Practice (0/3 done)

Task 1 · Question

Why does Text("$count") never change in a composable that uses var count = 0 and count++ on click?

Task 2 · Question

What is state hoisting?

Task 3 · Practice

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

Question 1

1.What does remember do?

Question 2

2.What is recomposition?

Question 3

3.Which survives screen rotation?

Question 4

4.Why should composables avoid side effects like network calls in their body?

Read more (free)

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.

From Interview questions: Android and Compose

Which function gets a ViewModel inside a composable (without Hilt)?Related

Answer: viewModel()

viewModel() returns the instance scoped to the screen.

From ViewModel and MVVM

Which survives a configuration change?Related

Answer: A ViewModel

ViewModels outlive configuration changes.

From Activities and the lifecycle

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.

From Hello Compose: your first screen

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

  1. 3 more topics in “Jetpack Compose UI” before its exam: Layouts: Column, Row, Box and Scaffold, Lists and grids, Text input and forms.
  2. Module 6 exam: Jetpack Compose UI · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
  3. Then the next module, Navigation and app architecture. 50 topics left in the course before the final exam.

← Previous: Composables and modifiers