Search code examples
androidandroid-jetpack-composecompose-multiplatform

How to scroll LazyColumn to the top and display BottomAppBar after clicking a button


This is my simplified code:

val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
val isCollapsed = scrollBehavior.state.collapsedFraction != 1.0f
val listState = rememberLazyListState()
Scaffold(
    contentWindowInsets = WindowInsets(0, 0, 0, 0),
    modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
    bottomBar = {
        AnimatedVisibility(!listState.canScrollBackward || !isCollapsed) {
            BottomAppBar(scrollBehavior = scrollBehavior) {
                //...
                Button(onClick = {
                    scope.launch {
                        listState.animateScrollToItem(0)
                    }
                }) {
                    Text("Scroll to top")
                }
            }
        }
    }
)

isCollapsed and !listState.canScrollBackward is used to determine whether the BottomAppBar should be displayed.

I saw such an implementation on issuetracker. But it not works for me.


Solution

  • As mentioned in the Issue Tracker, you can use a NestedScrollDispatcher to trigger displaying the BottomAppBar once the LazyColumn is scrolled to the top.

    You however need to use dispatchPostScroll instead of dispatchPreScroll according to how the BottomAppBarScrollBehavior is implemented. Please try the following code:

    @OptIn(ExperimentalMaterial3Api::class)
    @Composable
    fun BottomAppBarScaffold() {
        val coroutineScope = rememberCoroutineScope()
        val listState = rememberLazyListState()
        val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
        val bottomScrollDispatcher = NestedScrollDispatcher()
    
        Scaffold(
            modifier = Modifier.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection, bottomScrollDispatcher),
            bottomBar = {
                BottomAppBar(
                    scrollBehavior = bottomAppBarScrollBehavior,
                    actions = {
                        IconButton(onClick = { /* do something */ }) {
                            Icon(Icons.Filled.Check, contentDescription = "")
                        }
                        IconButton(onClick = { /* do something */ }) {
                            Icon(Icons.Filled.Edit, contentDescription = "")
                        }
                        IconButton(onClick = { /* do something */ }) {
                            Icon(Icons.Filled.Mic, contentDescription = "")
                        }
                    }
                )
            },
            floatingActionButton = {
                AnimatedVisibility(
                    visible = listState.canScrollBackward,
                    enter = fadeIn(),
                    exit = fadeOut()
                ) {
                    FloatingActionButton(
                        onClick = {
                            coroutineScope.launch {
                                bottomScrollDispatcher.dispatchPostScroll(
                                    consumed = Offset(0f, -bottomAppBarScrollBehavior.state.heightOffsetLimit),
                                    available = Offset.Zero,
                                    NestedScrollSource.SideEffect
                                )
                               listState.animateScrollToItem(0)
                            }
                        },
                        containerColor = BottomAppBarDefaults.bottomAppBarFabColor,
                        elevation = FloatingActionButtonDefaults.bottomAppBarFabElevation()
                    ) {
                        Icon(Icons.Filled.ArrowUpward, "")
                    }
                }
            },
            content = { paddingValues ->
                LazyColumn(
                    modifier = Modifier
                        .fillMaxSize()
                        .padding(paddingValues)
                        .nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection, bottomScrollDispatcher),
                    state = listState
                ) {
                    items(50) { item ->
                        Text("Item $item", modifier = Modifier.padding(16.dp))
                    }
                }
            }
        )
    }
    

    Output:

    Screen Recording