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

如何从NavDestination获取对应Composable,实现底部导航视图显示?

问题解决:Jetpack Compose底部导航与主页面显示异常

核心问题分析

你当前的代码存在两个关键问题:

  • NavHost 被放置在 Scaffold 外部,导致 Scaffold 的内容区域(innerPadding 对应的容器)为空,主页面内容无法显示
  • 手动维护的 selectedItemIndex 无法与导航控制器的实际状态同步,容易出现选中状态和当前页面不匹配的情况

修复步骤

1. 将NavHost移入Scaffold的Content区域

Scaffold 的 content 参数是专门承载主页面内容的区域,需要把 NavHost 放到该区域内,同时应用 innerPadding 避免内容被底部导航栏遮挡。

2. 让底部导航自动同步选中状态

无需手动维护 selectedItemIndex,直接通过 navController.currentDestination?.route 匹配导航项的路由,自动计算当前选中的索引,导航时选中状态会自动更新。

修改后的完整代码

调整BottomNavigationBar组件

移除传入的 selectedItemIndex,改为内部计算选中状态:

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomNavigationBar(navController: NavController, items: List<BottomNavigationItem>) {
    val currentRoute = navController.currentDestination?.route
    // 自动判断是否显示底部导航
    val showNavBar = items.any { it.route == currentRoute }

    if(showNavBar) {
        NavigationBar {
            items.forEachIndexed { index, item ->
                val isSelected = currentRoute == item.route
                NavigationBarItem(
                    selected = isSelected,
                    onClick = { 
                        // 避免重复导航
                        if (!isSelected) {
                            navController.navigate(item.route)
                        }
                    },
                    label = { Text(text = item.title) },
                    icon = {
                        BadgedBox(
                            badge = {
                                item.badgeCount?.takeIf { it != 0 }?.let {
                                    Text(text = it.toString())
                                }
                            }
                        ) {
                            Icon(
                                imageVector = if(isSelected) item.selectedIcon else item.unselectedIcon,
                                contentDescription = item.title
                            )
                        }
                    }
                )
            }
        }
    }
}

data class BottomNavigationItem(
    val title: String,
    val route: String,
    val selectedIcon: ImageVector,
    val unselectedIcon: ImageVector,
    val badgeCount: Int? = null,
)

修改AppView组件

把NavHost移入Scaffold的content区域,移除手动维护的selectedItemIndex:

@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppView() {
    val navController: NavHostController = rememberNavController()

    val items = listOf(
        BottomNavigationItem(
            title = stringResource(R.string.title_favourites),
            route = NavigationDestination.FavouritesView.route,
            selectedIcon = Icons.Filled.Favorite,
            unselectedIcon = Icons.Outlined.FavoriteBorder,
        ),
        BottomNavigationItem(
            title = stringResource(R.string.title_dialer),
            route = NavigationDestination.DialerView.route,
            selectedIcon = Icons.Filled.Phone,
            unselectedIcon = Icons.Outlined.Phone,
        ),
        BottomNavigationItem(
            title = stringResource(R.string.title_recents),
            route = NavigationDestination.RecentsView.route,
            selectedIcon = Icons.Filled.Alarm,
            unselectedIcon = Icons.Outlined.Alarm,
            badgeCount = 0
        ),
        BottomNavigationItem(
            title = stringResource(R.string.title_contacts),
            route = NavigationDestination.ContactsView.route,
            selectedIcon = Icons.Filled.Person,
            unselectedIcon = Icons.Outlined.Person,
        )
    )

    Scaffold(
        bottomBar = {
            BottomNavigationBar(
                navController = navController,
                items = items
            )
        }
    ) { innerPadding ->
        Box(
            modifier = Modifier
                .padding(innerPadding)
                .fillMaxSize()
        ) {
            // 将NavHost移到这里,作为Scaffold的主内容
            NavHost(
                navController = navController,
                startDestination = NavigationDestination.RecentsView.route
            ) {
                composable(route = NavigationDestination.RecentsView.route) {
                    RecentsView()
                }
                composable(route = NavigationDestination.FavouritesView.route) {
                    FavouritesView()
                }
                composable(route = NavigationDestination.DialerView.route) {
                    DialerView()
                }
                composable(route = NavigationDestination.ContactsView.route) {
                    ContactsView()
                }
                composable(route = NavigationDestination.AddContactView.route) {
                    AddContactView()
                }
            }
        }
    }
}

额外优化点

  • 给BottomNavigationItem的点击事件增加重复导航判断,避免重复入栈
  • 简化了showNavBar的判断逻辑,移除冗余的forEach循环
  • 简化BadgedBox的badge显示逻辑,用takeIf减少嵌套判断

内容的提问来源于stack exchange,提问作者JamieRhys

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 02:59:54