State in Jetpack Compose — For Someone Who Knows Flutter
The Problem with Plain Variables
In Compose, a plain variable doesn’t work the way you’d expect.
var count = 0Compose doesn’t watch this. When count changes, nothing happens on screen. The UI has no idea anything changed, it just keeps showing whatever it showed before.
To make Compose react to a value, that value has to live in state:
var count by remember { mutableStateOf(0) }Now when count changes, Compose sees it and re-renders automatically. No manual call. No notification. Just the change itself triggers the update.
The Flutter Parallel
In Flutter, you hold state in a StatefulWidget and call setState() to trigger a rebuild:
setState(() {
count++;
});In Compose, you just change the value — and because it’s wrapped in mutableStateOf, Compose reacts on its own:
count++ // Compose sees this and recomposes automaticallyFlutter is imperative about state — you call setState() and tell it to rebuild. Compose is reactive — it watches the state object and recomposes whenever it changes.
The end result is the same. The mechanism is different.
What remember Does
Every time Compose runs a composable, it re-executes the function from scratch. Without remember, every re-composition would reset local variables back to their initial value — losing whatever state had built up.
Get Hafiz Umar Ahmad Siddiqui’s stories in your inbox
Join Medium for free to get updates from this writer.
remember holds a value across recompositions:
var count by remember { mutableStateOf(0) }
// ↑
// survives recompositionBoth parts work together:
mutableStateOf(0)makes the value observable — Compose watches it for changesremember { }makes it survive recomposition — the value isn't lost when the function re-executes
Remove either one and it breaks. Without mutableStateOf, Compose won't react. Without remember, the value resets every recomposition.
Re-composition Is Not a Full Redraw
Important distinction: re-composition does not mean the entire screen redraws.
When a state value changes, Compose only reruns the composables that depend on that specific state — not everything. The rest of the UI is left alone. It’s targeted and efficient.
And re-composition is triggered by state changes, not by events. You don’t tell Compose “redraw now.” You change a state value, and Compose reacts to that change on its own terms.
remember vs ViewModel
remember keeps state alive across re-compositions, but not across Activity recreation. If the device rotates, or the system triggers a configuration change, remember values are gone.
For state that needs to survive that:
remember → survives recomposition
lost when Activity is recreated (rotation, config change)ViewModel → survives re-composition
survives Activity recreation tooremember is for UI-local state — things that only matter within the current composition. ViewModel is for screen-level state — anything that should outlive the Activity.
The rule: if it needs to survive a screen rotation, it doesn’t belong in remember.









