【问题标题】:How can we hide BottomAppBar (with navigation include) when navigate to composable?当导航到可组合时,我们如何隐藏 BottomAppBar(包含导航)?
【发布时间】:2021-08-07 01:38:14
【问题描述】:

我有问题。

问题是当我导航到“添加问题”屏幕时,我不知道如何隐藏底部应用栏。

我需要你的帮助。

这是带有底部应用栏的 MyScreen

@Composable
fun Navigation() {
    val navController = rememberNavController()
    val items = listOf(Screen.Home, Screen.Search, Screen.Notifications, Screen.Profil)

    Scaffold(
        bottomBar = {
            bottomAppNavigation(navController = navController, items)

        }
    ) {
        Box(modifier = Modifier.padding(it)) {
            ScreenController(navController = navController)
        }

    }
}

这是我的带有 navHost 的控制器

@ExperimentalComposeUiApi
@Composable
fun ScreenController(navController: NavHostController) {
    NavHost(navController = navController, startDestination = Screen.Home.route) {
        composable(Screen.Home.route) {
            HomeScreen(navController)
        }
        composable(Screen.Search.route) {
            SearchScreen(navController, it)
        }
        composable(Screen.Notifications.route) {

        }
        composable(Screen.Profil.route) {
            user_profil()
        }
        composable("Ask_question") {
            AskScreen(navController)
        }
    }
}

我认为问题是因为这就像活动和片段,我有一个可组合屏幕所在的盒子,我所有的页面都在他里面。

【问题讨论】:

    标签: android kotlin navigation android-jetpack-compose


    【解决方案1】:

    我建议您将AnimatedVisibility 用于BottomNavigation 小部件,我认为这是最清晰的撰写方式。

    1. 你应该使用remeberSaveable来存储BottomBar的状态:
    // State of bottomBar, set state to false, if current page route is "car_details"
    val bottomBarState = rememberSaveable { (mutableStateOf(true)) }
    
    1. 在composable函数中,我们使用when来控制BottomBar的状态,下面我们将bottomBarState设置为true,如果要显示BottomBar,则将bottomBarState设置为false
    val navController = rememberNavController()
    
    // Subscribe to navBackStackEntry, required to get current route
    val navBackStackEntry by navController.currentBackStackEntryAsState()
    
    // Control BottomBar
    when (navBackStackEntry?.destination?.route) {
        "cars" -> {
            // Show BottomBar
            bottomBarState.value = true
        }
        "bikes" -> {
            // Show BottomBar
            bottomBarState.value = true
        }
        "settings" -> {
            // Show BottomBar
            bottomBarState.value = true
        }
        "car_details" -> {
            // Hide BottomBar
            bottomBarState.value = false
        }
    }
    
    Scaffold(
        bottomBar = {
            BottomBar(
                navController = navController,
                bottomBarState = bottomBarState
            )
        },
        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,
                    )
                }
            }
        }
    )
    
    1. BottomNavigation 放入AnimatedVisibility 中,从bottomBarState 设置visible 值并设置enterexit 动画,在我的例子中,我使用slideInVertically 代表enter 动画和@98765434 exit动画:
    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
                                }
                            }
                        )
                    }
                }
            }
        )
    

    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)) }
    
        BottomBarAnimationTheme {
            val navController = rememberNavController()
    
            // Subscribe to navBackStackEntry, required to get current route
            val navBackStackEntry by navController.currentBackStackEntryAsState()
    
            // Control BottomBar
            when (navBackStackEntry?.destination?.route) {
                "cars" -> {
                    // Show BottomBar
                    bottomBarState.value = true
                }
                "bikes" -> {
                    // Show BottomBar
                    bottomBarState.value = true
                }
                "settings" -> {
                    // Show BottomBar
                    bottomBarState.value = true
                }
                "car_details" -> {
                    // Hide BottomBar
                    bottomBarState.value = false
                }
            }
    
            Scaffold(
                bottomBar = {
                    BottomBar(
                        navController = navController,
                        bottomBarState = bottomBarState
                    )
                },
                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,
                            )
                        }
                    }
                }
            )
        }
    }
    
    @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 注解。

    更新: Compose 1.1.0 及以上版本@ExperimentalAnimationApi 不需要。

    22.02.2022 更新:我做了一些研究,更新点2。现在我们使用when控制bottomBarState

    完整代码可在 gitHub 上找到: https://github.com/AndreiRoze/BottomBarAnimation

    官方文档中提供的动画示例: https://developer.android.com/jetpack/compose/animation

    【讨论】:

    • 很好。我尝试过类似地实现它(使用 OnDestinationChangedListener 的 DisposableEffect),但 BottomBar 根本没有动画。有什么想法吗?
    • @bompf 您是否尝试更改 OnDestinationChangedListener 中的 BottomBarState 值?
    • 我的错误。我用if (isVisible) ... 包裹了AnimatedVisibility。现在可以了。
    • @AndreiR,根据您的解决方案,您将 navController 传递给每个可用于导航到其他可组合物的可组合物。如果车载屏幕有更多的按钮和可点击的组合,那不会造成混乱吗?在这种情况下,可组合项可能会被分成单独的文件夹和包。并且将 navController 传递给它们中的每一个肯定会造成混乱,对吧?
    • @FahimHoque,就我而言,我使用简单的导航方式。关于复杂的案件 - 你是对的。如果你有很多路线,我推荐阅读这篇关于导航的文章medium.com/google-developer-experts/…。在文章末尾,您可以阅读组织导航的不同解决方案。
    【解决方案2】:

    如果您想隐藏BottomBar,请不要输出它。

    类似:

    Scaffold(
        bottomBar = {
            if (currentRoute != "xxxx") {
                BottomAppBar() {
                   //....
                }
            }
        },
    

    currentRoute 在哪里(至少使用 Navigation Compose 2.4.0-alpha01):

    val navBackStackEntry by navController.currentBackStackEntryAsState()
    val currentRoute = navBackStackEntry.destination.route
    

    【讨论】:

    • 哦...好吧,但是什么是 currentRoute ?因为我做了这个:val currentRoute = navController.currentBackStackEntry?.arguments?.getString(KEY_ROUTE),但它没有用
    • @Mehdi.ncb 我已经更新了答案。查看官方文档:developer.android.com/jetpack/compose/navigation#bottom-nav
    • 你回答了,但是,这是最好的方法吗?就像,当我回去时,我看到我的底部导航栏从底部出现(小动画 - 0.2 秒)。那么,您对此有最佳选择吗?
    • @Mehdi.ncb 老实说,我不知道是否有更好的选择,但我认为使用当前的 API 是正确的方法。
    • 好的,非常感谢,您在 stackOverflow 上帮助了很多人
    猜你喜欢
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-09
    • 1970-01-01
    • 2021-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多