Reputation: 13317
I'm looking forward to a way to implement a Collapsing effect while scrolling on LazyColumn
list. I have been checking the docs but I didn't found nothing related. How can I implement it?
At the moment I'm using a BottomNavigation
setted inside my Scaffold
and I can add the inner paddings to the screen coming from the scaffold content lambda. But I didn't find any kind of scrollable state or something close to it.
Upvotes: 9
Views: 6300
Reputation: 10333
There now is a dedicated BottomAppBarDefaults.exitAlwaysScrollBehavior()
that you can use.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SimpleBottomAppBar() {
val bottomAppBarScrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(bottomAppBarScrollBehavior.nestedScrollConnection),
bottomBar = {
BottomAppBar(
windowInsets = WindowInsets.navigationBars,
scrollBehavior = bottomAppBarScrollBehavior,
actions = {
IconButton(onClick = { /* do something */ }) {
Icon(Icons.Filled.Check, contentDescription = "")
}
}
)
}
) { paddingValues ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
items(50) { item ->
Text("Item $item", modifier = Modifier.padding(16.dp))
}
}
}
}
Output:
Upvotes: 0
Reputation: 13129
This is the way I did it
val bottomBarHeight = remember { mutableStateOf(0f) }
val bottomBarOffsetHeightPx = remember { mutableStateOf(0f) }
val showBottomBar = remember { mutableStateOf(true) }
val nestedScrollConnection = remember {
object : NestedScrollConnection {
override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
val delta = available.y
val newOffset = bottomBarOffsetHeightPx.value + delta
bottomBarOffsetHeightPx.value = newOffset.coerceIn(-bottomBarHeight.value, 0f)
showBottomBar.value = newOffset >= 0f
return Offset.Zero
}
}
}
Scaffold(
modifier = Modifier.nestedScroll(nestedScrollConnection),
contentWindowInsets = WindowInsets.safeDrawing,
topBar = { TopAppBarComposable(navController = navController) },
bottomBar = {
AnimatedVisibility(
visible = showBottomBar.value,
enter = slideInVertically(
initialOffsetY = { it },
animationSpec = tween(durationMillis = 150)
),
exit = slideOutVertically(
targetOffsetY = { it },
animationSpec = tween(durationMillis = 150)
)
) {
BottomBarNavigation(
modifier = Modifier
.onGloballyPositioned { coordinates ->
bottomBarHeight.value = coordinates.size.height.toFloat()
},
navController = navController
)
}
},
containerColor = Color.Black
) { ...
I also added onGloballyPositioned
which will give us the real height of the bottomnav without hardcoding it.
This way we also hide the bottom nav, you will see a small bug in the background, that is what I was trying to fix but I did not find anything yet. I think it has to do with the recomposition and it's a little bit tricky to do it in real-time while the animation plays. If someone finds a solution, please add it to the answer.
Upvotes: 0
Reputation: 363439
You can use the nestedScroll
modifier.
Something like:
val bottomBarHeight = 48.dp
val bottomBarHeightPx = with(LocalDensity.current) { bottomBarHeight.roundToPx().toFloat() }
val bottomBarOffsetHeightPx = remember { mutableStateOf(0f) }
// 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 {
val delta = available.y
val newOffset = bottomBarOffsetHeightPx.value + delta
bottomBarOffsetHeightPx.value = newOffset.coerceIn(-bottomBarHeightPx, 0f)
return Offset.Zero
}
}
}
and then apply the nestedScroll
to the Scaffold:
Scaffold(
Modifier.nestedScroll(nestedScrollConnection),
scaffoldState = scaffoldState,
//..
bottomBar = {
BottomAppBar(modifier = Modifier
.height(bottomBarHeight)
.offset { IntOffset(x = 0, y = -bottomBarOffsetHeightPx.value.roundToInt()) }) {
IconButton(
onClick = {
coroutineScope.launch { scaffoldState.drawerState.open() }
}
) {
Icon(Icons.Filled.Menu, contentDescription = "Localized description")
}
}
},
content = { innerPadding ->
LazyColumn(contentPadding = innerPadding) {
items(count = 100) {
Box(
Modifier
.fillMaxWidth()
.height(50.dp)
.background(colors[it % colors.size])
)
}
}
}
)
Upvotes: 16