androidandroid-jetpack-composevertical-scrollinglazycolumn

Compose LazyColumn scrolling behavior inside of CoordinatorLayout via ComposeView interop


Problem - scrolling downward causes the bottom sheet to scroll rather than giving scroll priority to the LazyColumn (RecyclerView did not have this problem. It was wrapped by a NestedScrollView)

I've just introduced a Compose LazyColumn replacement of a Recycler inside of a CoordinatorLayout. The Coordinator (implemented as a bottom sheet) can itself scroll freely between peek and expanded states. My issue is when dragging the items area downward in the LazyColumn, the bottom sheet picks up the scrolling rather than the LazyColumn . If I scroll upward first and then downward (without releasing) on the LazyColumn, the scrolling is picked up by the LazyColumn and scrolling priority is given to the LazyColumn (expected behavior.)

BottomSheetFragment
|-CoordinatorLayout
|--ConstraintLayout (BottomSheetBehavior)
|---MyListFragment
|----ComposeView
|-----Theme
|------Surface
|-------Box
|--------LazyColumn

New to Compose, so I'm hoping someone can tell me how to approach correcting this new scroll behavior?

**Edit I'm getting part of the way to having this work by toggling the Coordinator's ^^ BottomSheetBehavior.isDragglable, but it does require that I release the drag rather than smoothly transitioning from the list scroll to the bottom sheet scroll - anyone suggest a fix?:

fun MyUi(listener:Listener) {
    val listState = rememberLazyListState()

    LaunchedEffect(listState) {
        listState.interactionSource.interactions.collect {
            //at the top of the list so allow sheet scrolling
            listener.allowSheetDrag(listState.firstVisibleItemScrollOffset == 0)
        }
    }

    val nestedScrollConnection = remember {
        object : NestedScrollConnection {
            override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
                Timber.i("NestedScrollConnection onPreScroll($available: Offset, $source: NestedScrollSource)")
                return super.onPreScroll(available, source)
            }

            override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
                Timber.i("NestedScrollConnection onPostScroll($consumed: Offset, $available: Offset, $source: NestedScrollSource)")
                if (available.y > 0.0 && consumed.y == 0.0f) {
                    //scolling down up but we're already at the top - kick over to sheet scrolling
                    listener.allowSheetDrag(true)
                }
                return super.onPostScroll(consumed, available, source)
            }
        }
    }
    Box(
        modifier = Modifier
            .fillMaxSize()
            .nestedScroll(nestedScrollConnection)
    ) {
        LazyColumn(
            modifier =
            Modifier
                .fillMaxSize()
                .padding(vertical = 12.dp), state = listState
        ) {
            item {
                Row() {}
            }
        }
    }
}

And then in the Fragment:

override fun allowSheetDrag(allowSheetDrag: Boolean) {
    bottomSheetFragment?.bottomSheetBehavior?.isDraggable = allowSheetDrag
}

Solution

  • Wizard Chris Banes just recently published a workaround, which I can confirm it works.

    It also transitions nicely without lifting a finger, when being used inside a BottomSheetDialog (scrolling up, then dragging the sheet down in one go)

    Example Usage (taken from Chris Banes' example):

    setContent {
        Surface(
            // Add this somewhere near the top of your layout, above any scrolling layouts
            modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())
        ) {
            LazyColumn() {
                // blah
            }
        }
    }
    

    There is an issue being tracked for nested scrolling with ComposeViews: https://issuetracker.google.com/issues/174348612, and a related SO question which lead me there: AndroidView in Compose loses touch events in NestedScrollView