You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose导航至子页面时BottomNavigation项保持选中方案

问题根源

你对NavDestination.hierarchy的理解存在偏差:该属性遍历的是 当前目的地到所属各级导航图的层级链,而非返回栈中的历史目的地列表。
你目前将GameDetailScreen作为根NavHost下的平级路由声明,它的层级链仅包含自身、根NavGraph节点,完全不包含GameListScreen节点,自然无法匹配到Search Tab对应的路由,导致选中状态丢失。

实现方案

方案1:嵌套导航图(官方推荐写法)

将同属一个底部Tab的所有页面归到同一个嵌套导航图下,给嵌套图设置和对应Tab项完全一致的路由,此时子页面的层级链会自动包含嵌套图的路由,无需修改选中判断逻辑:
修改NavHost的路由声明:

@Composable
fun Navigation(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = Screen.Home.route
    ) {
        composable(Screen.Home.route) {
            HomeScreen()
        }
        // 搜索Tab对应的嵌套导航图,根路由和Tab绑定路由一致
        navigation(
            startDestination = Screen.GameListScreen.route,
            route = Screen.GameListScreen.route
        ) {
            composable(Screen.GameListScreen.route) {
                GameListScreen(navController)
            }
            composable(
                route = "${Screen.GameDetailScreen.route}/{gameId}",
                arguments = listOf(navArgument("gameId") { type = NavType.IntType })
            ) {
                GameDetailScreen()
            }
        }
        composable(Screen.Profile.route) {
            ProfileScreen()
        }
    }
}

修改完成后,进入游戏详情页时,currentDestination.hierarchy会包含上层嵌套图的路由(即Search Tab绑定的Screen.GameListScreen.route),原有选中判断逻辑会自动生效,对应Tab保持选中。

方案2:路由映射匹配

如果不想调整现有导航结构,可以手动维护每个Tab对应的子路由集合,匹配时判断当前页面是否属于对应Tab的路由范围:

  1. 扩展BottomNavItem数据结构,增加子路由字段
data class BottomNavItem(
    val name: String,
    val route: String,
    val icon: ImageVector,
    val badgeCount: Int = 0,
    val childRoutes: List<String> = emptyList() // 该Tab下所有子页面路由
)
  1. 初始化导航项时,给Search Tab配置子路由
BottomNavItem(
    name = "Search",
    route = Screen.GameListScreen.route,
    icon = Icons.Default.Search,
    childRoutes = listOf("${Screen.GameDetailScreen.route}/{gameId}")
)
  1. 修改BottomNavigationItem的选中逻辑
val currentRoute = navBackStackEntry?.destination?.route
BottomNavigationItem(
    selected = currentRoute == item.route || 
               item.childRoutes.any { routePattern ->
                   currentRoute?.matches(Regex(routePattern)) == true
               },
    // 其余参数保持原有逻辑即可
    onClick = { onItemClick(item) },
    // ...
)
额外注意

从列表页跳详情页时,不要复用底部Tab的跳转配置(即不要加popUpTo到起始目的地、restoreState那套逻辑),直接调用navController.navigate("${Screen.GameDetailScreen.route}/$gameId")即可,否则会将列表页弹出返回栈,按返回键时直接回到首页,不符合交互预期。


内容的提问来源于stack exchange,提问作者Thomas Göhringer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 08:51:58