Colton Idle
08/18/2026, 12:35 PMfun updateStuff(x: String, y: String, z: String ){
email = x
phone = y
address = z
}
email, phone and address is snapshot state
is that 3 recompositions? or just 1 recompositionColton Idle
08/18/2026, 12:40 PMFilip Wiesner
08/18/2026, 12:43 PMColton Idle
08/18/2026, 12:44 PMFilip Wiesner
08/18/2026, 12:52 PMColton Idle
08/18/2026, 12:58 PMColton Idle
08/18/2026, 12:58 PMFilip Wiesner
08/18/2026, 1:07 PMColton Idle
08/18/2026, 1:44 PMPablichjenkov
08/18/2026, 2:58 PMromainguy
08/18/2026, 2:59 PMColton Idle
08/18/2026, 3:08 PMPablichjenkov
08/18/2026, 4:35 PMJonathan
08/18/2026, 5:14 PM.copy(…) function is the more technically correct approach (assumes your Ui state object is a data class)? It guarantees UI state changes are atomic and it would be slightly more performant instead of 3 separate calls MutableState.setValue(…) with the individual changing values, which will not be amortized (only the “scheduled UI draw”).mzgreen
08/18/2026, 6:04 PMChuck Jazdzewski [G]
08/19/2026, 4:07 PMupdateStuff will cause 3 recompositions.
When a change is made in the global snapshot it triggers an eventual "advance" of the global snapshot that will send the set of all changes since the last time it advanced to all listeners. The Recomposer, upon receiving the first such notification since the last frame, will schedule a new frame (assuming any of the changes were observed by a composition). Until the frame starts, all changes will accumulate in the Recomposer until the frame starts. When the frame starts the Recomposer request the snapshot system to send, synchronously, any pending changes. Then a snapshot is taken and all external changes are ignored while a composition is running (using snapshot isolation). Any changes that occur during the composition will be treated as a change that occurred between the current frame and the next one (i.e. it will cause the Recomposer to loop and request another frame).
This naturally batches changes that occur in the global snapshot and they are processed on the cadence of the monotonic frame clock of the Recomposer which is driven by the `Choreographer`in Android. By creating a snapshot the values of all mutable state are consistent with a single point in time.
If you have a background thread and want all the changes to appear atomically to the composition, make the changes in a mutable snapshot and apply the changes when complete. The changes are isolated from the global snapshot until the snapshot is applied and then they appear as one atomic change. In other words, if updateStuff() is called in snapshot in a background thread you will never see a state where the update is partially complete. Either you will see all the changes of updateStuff() or none of them.
Creating a single mutable state object that contains all the values, or mutable state for each field, does not affect the atomic, isolated and consistent properties of the object. All mutable state objects have these properties and are atomic, isolated and consistent based on the current snapshot they are viewed from. Also, as noted above, changing any or all the mutable state objects between frame will be process by the next frame all at once.
As noted above, this is not free. Writes to the global snapshot are observed by the GlobalSnapshotManager whose job it is to schedule the change notifications. Reads are observed by the composer and the modifiers (during layout and draw) which tracks the reads in hash tables to be able to map the recompose scope or modifier node that was affected by a change. Also, to provide isolation, a "state record" is created (or reused) to store the isolated state of a snapshot for any mutable state object that is written to.Jonathan
08/19/2026, 4:13 PMupdateAll() one after another, is it at all possible for a snapshot to be taken in-between the individual calls and the UI reflects a briefly inconsistent state? More specifically, if updateAll() is called from a non-UI thread?Chuck Jazdzewski [G]
08/19/2026, 5:50 PMphone or address may still invalidate the component displaying address but not email). This is partially mitigated by slots and lambda capture (we invalidate the lambda invocation, not all the code between the lambda creation and the invocation). For example, capturing the immutable object the content lambda of a Button will invalidate just the content lambda, not the Button itself, If each field, on the other hand, where a mutableStateOf only the reader of email would be invalidated.
In general, my recommendation is, if values always change together, make them one value in a single mutableStateOf. If values change independently, use a mutableStateOf for each independent value. This is not a hard rule, more of a guideline which should be validated by tracing and adjusted accordingly, as it can very greatly depending on the structure of the data and how it is read.