Compose state deferred reads
Core principle
State reads invalidate the phase that reads them. If a
State
is read in a composable body, changes invalidate composition. If it is read in layout or draw, changes can invalidate only layout or draw. Frame-rate state such as scroll offsets, animations, and drag positions usually belongs in layout/draw, not composition.
Back-writing
is the symmetric failure mode: writing observable state from a phase that triggers invalidation of an earlier phase. Compose phases run composition → layout → draw. Writing snapshot-backed state from layout or draw to state read in composition invalidates composition; writing during composition to state read earlier in the same composition does the same. Both schedule extra work — often cascading into sibling lazy items.
The fix is structural: keep the
State
or a provider lambda and read the value inside a layout/draw callback; capture measurements in callbacks and apply them in the measure phase, not by reading measurement state in sibling composable bodies.
When to use this skill
val x by animate*AsState(...)
is passed to
Modifier.offset(x = ...)
,
Modifier.size(...)
,
Modifier.graphicsLayer(...)
, or another value-form modifier.
LazyListState.firstVisibleItemScrollOffset
,
ScrollState.value
,
Animatable.value
, or gesture state is read in a composable body.
A composable takes
scrollOffset: Int
,
progress: Float
,
dragOffset: Offset
, or similar frame-rate values.
Recomposition counters climb during scroll, animation, or gestures even when data is stable.
A composable body calls
stateMap[key] = …
,
list.addAll(…)
, or similar on every recomposition (back-writing composition → composition).
One lazy item captures size with
onSizeChanged
/
onGloballyPositioned
and a sibling reads that height in composition (
Modifier.height(state.dp)
) — back-writing layout → composition.
Show more
Installs
426
Repository
chrisbanes/skills
GitHub Stars
732
First Seen
May 12, 2026
Security Audits
Gen Agent Trust Hub
Pass
Socket
Pass
Snyk
Pass