Reputation: 61
I'm creating a simple app with bottom navigation bar. I want to hide bottom navigation bar while scrolling down and show it while scrolling up in a composable screen.
Any help would be appreciated. Please do let me know if you need any more code. I have attached all the code that I think are relevant to this problem.
This is my bottom navigation bar.
@Composable
fun BottomBar(navController: NavController) {
val items = listOf(
NavigationItem.Home,
NavigationItem.Search
)
BottomNavigation(
backgroundColor = MaterialTheme.colors.DarkRed,
contentColor = Color.White
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
BottomNavigationItem(
selected = currentRoute == item.route,
icon = {
Icon(
imageVector = item.icon,
contentDescription = "Icon",
modifier = Modifier.size(28.dp)
)
},
alwaysShowLabel = false,
selectedContentColor = Color.White,
unselectedContentColor = Color.White.copy(0.4f),
onClick = {
navController.navigate(item.route){
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items
navController.graph.startDestinationRoute?.let{route ->
popUpTo(route){
saveState = true
}
}
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
}
}
)
}
}
}
This is my Main Screen
@Composable
fun MainScreen(){
val navController = rememberNavController()
Scaffold(topBar = {
ActionBar("Books")
},
bottomBar = {
BottomBar(navController)
}){
NavigationGraph(navController = navController)
}
}
And this NavigationGraph
@Composable
fun NavigationGraph(navController: NavHostController){
NavHost(navController = navController, startDestination = NavigationItem.Home.route ){
composable(NavigationItem.Home.route){
HomeScreen()
}
composable(NavigationItem.Search.route){
SearchScreen()
}
}
}
Upvotes: 5
Views: 2909
Reputation: 13159
This is still experimental, but you can use the latest version of Material3 in which you have scrollBehavior
inside BottomAppBar
. Just use
/**
* <a href="https://m3.material.io/components/bottom-app-bar/overview" class="external" target="_blank">Material Design bottom app bar</a>.
*
* A bottom app bar displays navigation and key actions at the bottom of mobile screens.
*
* 
*
* If you are interested in displaying a [FloatingActionButton], consider using another overload.
*
* Also see [NavigationBar].
*
* @param modifier the [Modifier] to be applied to this BottomAppBar
* @param containerColor the color used for the background of this BottomAppBar. Use
* [Color.Transparent] to have no color.
* @param contentColor the preferred color for content inside this BottomAppBar. Defaults to either
* the matching content color for [containerColor], or to the current [LocalContentColor] if
* [containerColor] is not a color from the theme.
* @param tonalElevation when [containerColor] is [ColorScheme.surface], a translucent primary color
* overlay is applied on top of the container. A higher tonal elevation value will result in a
* darker color in light theme and lighter color in dark theme. See also: [Surface].
* @param contentPadding the padding applied to the content of this BottomAppBar
* @param windowInsets a window insets that app bar will respect.
* @param scrollBehavior a [BottomAppBarScrollBehavior] which holds various offset values that will
* be applied by this bottom app bar to set up its height. A scroll behavior is designed to
* work in conjunction with a scrolled content to change the bottom app bar appearance as the
* content scrolls. See [BottomAppBarScrollBehavior.nestedScrollConnection].
* @param content the content of this BottomAppBar. The default layout here is a [Row],
* so content inside will be placed horizontally.
*/
@ExperimentalMaterial3Api
@Composable
fun BottomAppBar(
modifier: Modifier = Modifier,
containerColor: Color = BottomAppBarDefaults.containerColor,
contentColor: Color = contentColorFor(containerColor),
tonalElevation: Dp = BottomAppBarDefaults.ContainerElevation,
contentPadding: PaddingValues = BottomAppBarDefaults.ContentPadding,
windowInsets: WindowInsets = BottomAppBarDefaults.windowInsets,
scrollBehavior: BottomAppBarScrollBehavior? = null,
content: @Composable RowScope.() -> Unit
) {
And then in your scaffold
val scrollBehavior = BottomAppBarDefaults.exitAlwaysScrollBehavior()
CryptoTheme {
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
bottomBar = {
BottomBarNavigation(
navController = navController,
scrollBehavior = scrollBehavior
)
},
containerColor = Color.Black
) { ...
With this, we don't need to do anything else.
My bottombar composable is
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomBarNavigation(modifier: Modifier = Modifier, navController: NavController,
scrollBehavior: BottomAppBarScrollBehavior? = null
) {
...
BottomAppBar(
modifier = modifier.fillMaxWidth(),
containerColor = TransparentBackground,
scrollBehavior = scrollBehavior
) { ... } }
Upvotes: 1
Reputation: 46
using Gautam Hazarika answer you can do this for better performance.
val shouldHideBottomBar by remember(scrollState) {
derivedStateOf { scrollState.firstVisibleItemIndex == 0
}
Scaffold (
...
bottomBar = {
AnimatedVisibility(
visible = shouldHideBottomBar,
enter = slideInVertically(animationSpec = tween(durationMillis = 200)),
exit = slideOutVertically(animationSpec = tween(durationMillis = 200)), ) {
HomeBottomNavigation(bottomTab, setCurrentBottomTab)
}
},
)
{ ... }
Upvotes: 0
Reputation: 181
You can use a LazyListState
to track the state of the list and only show the BottomBar
when the scrollState index is initial. This can be done by:
For LazyColumn:
@Composable
fun HomeScreen(scrollState: LazyListState) {
LazyColumn(
state = scrollState,
) {
...
}
}
For NavigationGraph:
@Composable
fun NavigationGraph(navController: NavHostController, scrollState: LazyListState){
NavHost(navController = navController, startDestination = NavigationItem.Home.route ){
composable(NavigationItem.Home.route){
HomeScreen(scrollState = scrollState)
}
composable(NavigationItem.Search.route){
SearchScreen()
}
}
}
In the MainScreen:
@Composable
fun MainScreen(){
val navController = rememberNavController()
val scrollState = rememberLazyListState()
Scaffold(topBar = {
ActionBar("Books")
},
bottomBar = {
if(scrollState.firstVisibleItemIndex == 0){
BottomBar(navController)
}
}){
NavigationGraph(navController = navController, scrollState = scrollState)
}
}
This way the BottomBar
will only show up when the list is at top.
Upvotes: 2