Seri
09/18/2024, 8:50 PMSean Proctor
09/18/2024, 9:55 PMSean Proctor
09/18/2024, 9:55 PM@Composable
fun NestedScrollOffsetBox(
    toolbarHeight: Dp,
    modifier: Modifier = Modifier,
    content: @Composable NestedBoxScope.() -> Unit,
) {
// here we use LazyColumn that has build-in nested scroll, but we want to act like a
    // parent for this LazyColumn and participate in its nested scroll.
    // Let's make a collapsing toolbar for LazyColumn
    val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() }
    // our offset to collapse toolbar
    val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
    // now, let's create connection to the nested scroll system and listen to the scroll
    // happening inside child LazyColumn
    val nestedScrollConnection = remember {
        object : NestedScrollConnection {
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                // try to consume before LazyColumn to collapse toolbar if needed, hence pre-scroll
                val delta = available.y
                val newOffset = toolbarOffsetHeightPx.value + delta
                toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
                // here's the catch: let's pretend we consumed 0 in any case, since we want
                // LazyColumn to scroll anyway for good UX
                // We're basically watching scroll without taking it
                return Offset.Zero
            }
        }
    }
    Box(modifier.clipToBounds().fillMaxSize().nestedScroll(nestedScrollConnection)) {
        with(NestedBoxScopeImpl(this, toolbarOffsetHeightPx.value)) {
            content()
        }
    }
}
interface NestedBoxScope : BoxScope {
    fun Density.getOffset(): IntOffset
}
class NestedBoxScopeImpl(
    private val boxScope: BoxScope,
    private val offsetPx: Float,
) : NestedBoxScope {
    override fun Modifier.align(alignment: Alignment): Modifier {
        return with(boxScope) { this@align.align(alignment) }
    }
    override fun Modifier.matchParentSize(): Modifier {
        return with(boxScope) { this@matchParentSize.matchParentSize() }
    }
    override fun Density.getOffset(): IntOffset {
        return IntOffset(x = 0, y = offsetPx.roundToInt())
    }
}Sean Proctor
09/18/2024, 9:58 PMval height = 72.dp
NestedScrollOffsetBox(height) {
            List()
            Header(
                modifier = Modifier
                    .height(height)
            )
        }Seri
09/18/2024, 9:59 PMExitUntilCollapsedScrollBehavior
 and in this article on nested scrolling. Adapting these seems like a good approach!
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/[…]rScrollBehavior&ss=androidx%2Fplatform%2Fframeworks%2Fsupport
https://medium.com/androiddevelopers/understanding-nested-scrolling-in-jetpack-compose-eb57c1ea0af0