[英]hide Top and Bottom Navigator on a specific screen inside Scaffold Jetpack Compose
I'm creating a simple app with bottom navigation and drawer.我正在创建一个带有底部导航和抽屉的简单应用程序。
I wrap all screens inside a Scaffold with topbar and bottom bar.我将所有屏幕包裹在带有顶栏和底栏的脚手架内。 I want to hide top bar and bottom bar on a specific screen.
我想在特定屏幕上隐藏顶栏和底栏。 Does anyone know to how achieve that
有谁知道如何实现
here is the code for setting up navigation.这是设置导航的代码。
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
Scaffold(
bottomBar = {
AppBottomBar(navController)
},
topBar = {
AppTopBar(scaffoldState)
},
drawerContent = {
DrawerContent(navController, scaffoldState)
},
scaffoldState = scaffoldState
) {
// ovoid bottom bar overlay content
Column(modifier = Modifier.padding(bottom = 58.dp)) {
AppNavigation(navController)
}
}
AppNavigation
contains NavHost
for navigating to screens AppNavigation
包含用于导航到屏幕的NavHost
I recommend you use AnimatedVisibility
for BottomNavigation
widget and TopAppBar
widget, im my opinion it's clearest way for compose.我建议您将
AnimatedVisibility
用于BottomNavigation
小部件和TopAppBar
小部件,我认为这是最清晰的撰写方式。
remeberSaveable
to store state of BottomBar and TopAppBar:remeberSaveable
来存储 BottomBar 和 TopAppBar 的 state:// State of bottomBar, set state to false, if current page route is "car_details"
val bottomBarState = rememberSaveable { (mutableStateOf(true)) }
// State of topBar, set state to false, if current page route is "car_details"
val topBarState = rememberSaveable { (mutableStateOf(true)) }
when
for control state of BottomBar and TopAppBar, below we set bottomBarState
and topBarState
to true
, if we would like to show BottomBar and TopAppBar, otherwise we set bottomBarState
and topBarState
to false
:when
,下面我们将bottomBarState
和topBarState
设置为true
,如果我们想显示 BottomBar 和 TopAppBar,否则我们将bottomBarState
和topBarState
设置为false
:val navController = rememberNavController()
// Subscribe to navBackStackEntry, required to get current route
val navBackStackEntry by navController.currentBackStackEntryAsState()
// Control TopBar and BottomBar
when (navBackStackEntry?.destination?.route) {
"cars" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"bikes" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"settings" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"car_details" -> {
// Hide BottomBar and TopBar
bottomBarState.value = false
topBarState.value = false
}
}
com.google.accompanist.insets.ui.Scaffold(
bottomBar = {
BottomBar(
navController = navController,
bottomBarState = bottomBarState
)
},
topBar = {
TopBar(
navController = navController,
topBarState = topBarState
)
},
content = {
NavHost(
navController = navController,
startDestination = NavigationItem.Cars.route,
) {
composable(NavigationItem.Cars.route) {
CarsScreen(
navController = navController,
)
}
composable(NavigationItem.Bikes.route) {
BikesScreen(
navController = navController
)
}
composable(NavigationItem.Settings.route) {
SettingsScreen(
navController = navController,
)
}
composable(NavigationItem.CarDetails.route) {
CarDetailsScreen(
navController = navController,
)
}
}
}
)
Important: Scaffold from Accompanist, initialized in build.gradle.重要提示: Accompanist 的脚手架,在 build.gradle 中初始化。 We use Scaffold from Accompanist, because we need full control of paddings, for example in default Scaffold from Compose we can't disable padding for content from top if we have TopAppBar.
我们使用 Accompanist 的 Scaffold,因为我们需要完全控制填充,例如,在 Compose 的默认 Scaffold 中,如果我们有 TopAppBar,我们不能禁用顶部内容的填充。 In our case it's required because we have animation for TopAppBar, content should be under TopAppBar and we manually control padding for each pages.
在我们的例子中,它是必需的,因为我们有用于 TopAppBar 的 animation,内容应该在 TopAppBar 下,我们手动控制每个页面的填充。 Documentation from Accompanist: https://google.github.io/accompanist/insets/ .
伴奏者的文档: https://google.github.io/accompanist/insets/ 。
BottomNavigation
inside AnimatedVisibility
, set visible
value from bottomBarState
and set enter
and exit
animation, in my case I use slideInVertically
for enter
animation and slideOutVertically
for exit
animation:BottomNavigation
放入AnimatedVisibility
,从bottomBarState
设置visible
值并设置enter
和exit
animation,在我的情况下,我使用slideInVertically
enter
animation 和slideOutVertically
exit
Z6F1C25ED1523962BZBBF:9AnimatedVisibility(
visible = bottomBarState.value,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it }),
content = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon),
contentDescription = item.title
)
},
label = { Text(text = item.title) },
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
)
TopAppBar
inside AnimatedVisibility
, set visible
value from topBarState
and set enter
and exit
animation, in my case I use slideInVertically
for enter
animation and slideOutVertically
for exit
animation:TopAppBar
放入AnimatedVisibility
,从topBarState
设置visible
值并设置enter
和exit
animation,在我的情况下,我使用slideInVertically
enter
animation 和slideOutVertically
exit
Z6F1C25ED1525062B1BBFEED9AnimatedVisibility(
visible = topBarState.value,
enter = slideInVertically(initialOffsetY = { -it }),
exit = slideOutVertically(targetOffsetY = { -it }),
content = {
TopAppBar(
title = { Text(text = title) },
)
}
)
Full code of MainActivity: MainActivity 的完整代码:
package codes.andreirozov.bottombaranimation
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.res.painterResource
import androidx.navigation.NavController
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import codes.andreirozov.bottombaranimation.screens.BikesScreen
import codes.andreirozov.bottombaranimation.screens.CarDetailsScreen
import codes.andreirozov.bottombaranimation.screens.CarsScreen
import codes.andreirozov.bottombaranimation.screens.SettingsScreen
import codes.andreirozov.bottombaranimation.ui.theme.BottomBarAnimationTheme
@ExperimentalAnimationApi
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
BottomBarAnimationApp()
}
}
}
@ExperimentalAnimationApi
@Composable
fun BottomBarAnimationApp() {
// State of bottomBar, set state to false, if current page route is "car_details"
val bottomBarState = rememberSaveable { (mutableStateOf(true)) }
// State of topBar, set state to false, if current page route is "car_details"
val topBarState = rememberSaveable { (mutableStateOf(true)) }
BottomBarAnimationTheme {
val navController = rememberNavController()
// Subscribe to navBackStackEntry, required to get current route
val navBackStackEntry by navController.currentBackStackEntryAsState()
// Control TopBar and BottomBar
when (navBackStackEntry?.destination?.route) {
"cars" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"bikes" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"settings" -> {
// Show BottomBar and TopBar
bottomBarState.value = true
topBarState.value = true
}
"car_details" -> {
// Hide BottomBar and TopBar
bottomBarState.value = false
topBarState.value = false
}
}
// IMPORTANT, Scaffold from Accompanist, initialized in build.gradle.
// We use Scaffold from Accompanist, because we need full control of paddings, for example
// in default Scaffold from Compose we can't disable padding for content from top if we
// have TopAppBar. In our case it's required because we have animation for TopAppBar,
// content should be under TopAppBar and we manually control padding for each pages.
com.google.accompanist.insets.ui.Scaffold(
bottomBar = {
BottomBar(
navController = navController,
bottomBarState = bottomBarState
)
},
topBar = {
TopBar(
navController = navController,
topBarState = topBarState
)
},
content = {
NavHost(
navController = navController,
startDestination = NavigationItem.Cars.route,
) {
composable(NavigationItem.Cars.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
CarsScreen(
navController = navController,
)
}
composable(NavigationItem.Bikes.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
BikesScreen(
navController = navController
)
}
composable(NavigationItem.Settings.route) {
// show BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = true
topBarState.value = true
}
SettingsScreen(
navController = navController,
)
}
composable(NavigationItem.CarDetails.route) {
// hide BottomBar and TopBar
LaunchedEffect(Unit) {
bottomBarState.value = false
topBarState.value = false
}
CarDetailsScreen(
navController = navController,
)
}
}
}
)
}
}
@ExperimentalAnimationApi
@Composable
fun BottomBar(navController: NavController, bottomBarState: MutableState<Boolean>) {
val items = listOf(
NavigationItem.Cars,
NavigationItem.Bikes,
NavigationItem.Settings
)
AnimatedVisibility(
visible = bottomBarState.value,
enter = slideInVertically(initialOffsetY = { it }),
exit = slideOutVertically(targetOffsetY = { it }),
content = {
BottomNavigation {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
BottomNavigationItem(
icon = {
Icon(
painter = painterResource(id = item.icon),
contentDescription = item.title
)
},
label = { Text(text = item.title) },
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
)
}
@ExperimentalAnimationApi
@Composable
fun TopBar(navController: NavController, topBarState: MutableState<Boolean>) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val title: String = when (navBackStackEntry?.destination?.route ?: "cars") {
"cars" -> "Cars"
"bikes" -> "Bikes"
"settings" -> "Settings"
"car_details" -> "Cars"
else -> "Cars"
}
AnimatedVisibility(
visible = topBarState.value,
enter = slideInVertically(initialOffsetY = { -it }),
exit = slideOutVertically(targetOffsetY = { -it }),
content = {
TopAppBar(
title = { Text(text = title) },
)
}
)
}
Result:结果:
Don't forget to use @ExperimentalAnimationApi annotation for compose functions.不要忘记使用 @ExperimentalAnimationApi 注释来编写函数。
Update: with Compose version 1.1.0 and above @ExperimentalAnimationApi
not required.更新:不需要使用 Compose 版本 1.1.0 及更高版本
@ExperimentalAnimationApi
。
22.02.2022 Update: I made some research, and update point 2. Now we use when
for control topBarState
and bottomBarState
. 22.02.2022 更新:我做了一些研究,更新了点 2。现在我们使用
when
来控制topBarState
和bottomBarState
。
Full code available on gitHub: https://github.com/AndreiRoze/BottomBarAnimation/tree/with_animated_topbar gitHub 上提供的完整代码: https://github.com/AndreiRoze/BottomBarAnimation/tree/with_animated_topbar
Examples of animations available in the official documentation: https://developer.android.com/jetpack/compose/animation官方文档中可用的动画示例: https://developer.android.com/jetpack/compose/animation
for now, I can achieve that by checking current route to show or hide bottomBar, topBar.现在,我可以通过检查当前路线来显示或隐藏底部栏、顶部栏来实现这一点。 But I think there's must be better solutions.
但我认为必须有更好的解决方案。 The way I wrap all screens inside Scaffold might not right.
我将所有屏幕包裹在 Scaffold 内的方式可能不对。
val navController = rememberNavController()
val scaffoldState = rememberScaffoldState(rememberDrawerState(DrawerValue.Closed))
Scaffold(
bottomBar = {
if (currentRoute(navController) != "Example Screen") {
AppBottomBar(navController)
}
},
topBar = {
AppTopBar(scaffoldState)
},
drawerContent = {
DrawerContent(navController, scaffoldState)
},
floatingActionButton = {
FloatingButton(navController)
},
scaffoldState = scaffoldState
) {
// ovoid bottom bar overlay content
Column(modifier = Modifier.padding(bottom = 58.dp)) {
AppNavigation(navController)
}
}
@Composable
public fun currentRoute(navController: NavHostController): String? {
val navBackStackEntry by navController.currentBackStackEntryAsState()
return navBackStackEntry?.arguments?.getString(KEY_ROUTE)
}
Easiest solution I found (without animation)我找到的最简单的解决方案(没有动画)
fun MainScreen(modifier: Modifier = Modifier) {
val navController = rememberNavController()
var showBottomBar by rememberSaveable { mutableStateOf(true) }
val navBackStackEntry by navController.currentBackStackEntryAsState()
showBottomBar = when (navBackStackEntry?.destination?.route) {
"RouteOfScreenA" -> false // on this screen bottom bar should be hidden
"RouteOfScreenB" -> false // here too
else -> true // in all other cases show bottom bar
}
Scaffold(
modifier = modifier,
bottomBar = { if (showBottomBar) MyBottomNavigation(navController = navController) }
) { innerPadding ->
MyNavHost(
navController = navController,
modifier = Modifier.padding(innerPadding)
)
}
}
you can use compositionlocal
, and as you wrap your mainActivity with the CompositionLocalProvider
, pass the supportActionBar to your child composable At the screen where you wish to hide the topBar, invoke the.hide() method at the top.您可以使用
compositionlocal
,并且当您使用CompositionLocalProvider
包装 mainActivity 时,将 supportActionBar 传递给您的子可组合项 在您希望隐藏 topBar 的屏幕上,调用顶部的 .hide() 方法。 See below:见下文:
data class ShowAppBar(val show: ActionBar?)
internal val LocalAppBar = compositionLocalOf<ShowAppBar>{ error("No ActionBar provided") }
In mainActivity, pass the ActionBar via在 mainActivity 中,通过 ActionBar
val showy = ShowAppBar(show = supportActionBar )
.....
CompositionLocalProvider(
LocalAppBar provides showy
) {
YourTheme {
yourApp()
}
}
Invoking at the screen >> LocalAppBar.current.show?.hide()
在屏幕上调用 >>
LocalAppBar.current.show?.hide()
This worked for me.这对我有用。 You get the current route from the currentRoute function and do a check in your bottombar composable to either hide or show the BottomNavigationView.
您从 currentRoute function 获取当前路线,并检查底部栏可组合以隐藏或显示 BottomNavigationView。
@Composable
fun currentRoute(navController: NavHostController): String? {
val navBackStackEntry by navController.currentBackStackEntryAsState()
return navBackStackEntry?.destination?.route
}
@Composable
fun MainScreenView() {
val navController = rememberNavController()
Scaffold(bottomBar = {
if (currentRoute(navController) != BottomNavItem.Setup.screen_route)
BottomNavigation(navController = navController)
}
) {
NavigationGraph(navController = navController)
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.