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.
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: