Skip to main content

Coordinate nested scrolling

Consume an ancestor effect​

Attach one stable NestedScrollConnection to the ancestor that owns the effect. Pre callbacks run before child consumption; post callbacks receive what the child consumed and what remains. Return only the signed distance or velocity actually consumed.

fun UiTreeBuilder.CollapsingToolbar(
collapseBy: (deltaY: Float) -> Float,
) {
val latestCollapseBy = rememberUpdatedState(collapseBy)
val connection = remember {
object : NestedScrollConnection {
override fun onPreScroll(
available: ScrollDelta,
source: NestedScrollSource,
): ScrollDelta {
return ScrollDelta(
x = 0f,
y = latestCollapseBy.value(available.y),
)
}
}
}

Column(modifier = Modifier.nestedScroll(connection)) {
Text("Collapsing toolbar")
ScrollableColumn {
repeat(40) { index -> Text("Row $index") }
}
}
}

Clamp application state inside collapseBy; the Android renderer also rejects non-finite or over-consumed results. Use NestedScrollSource when user input, fling continuation, and imperative side effects require different policy.

Dispatch a custom scroll source​

Pass a stable NestedScrollDispatcher to nestedScroll when custom gesture or programmatic code must enter the same parent chain. Dispatch pre-scroll first, consume the remainder locally, then dispatch post-scroll with the local consumption and remainder. Use the equivalent pre/post fling pair for velocity.

Lazy collections, pagers, eager scroll containers, PullToRefresh, and framework drag or transform pan participate in the same chain. A native AndroidView joins automatically only when its View implements Android nested scrolling; otherwise dispatch explicitly. See Modifier architecture for phase order, modifier ordering, AndroidX mapping, and the legacy native-fling limitation.