Jetpack Compose点击底部导航项应用崩溃问题求助
问题原因分析
你遇到的崩溃核心原因是:在BottomNavigation组件内部新建了一个NavHostController,但这个控制器从未和任何NavHost绑定(即没有设置导航图graph),当点击导航项尝试访问navController.graph时,就会触发You must call setGraph() before calling getGraph()的异常。
简单说就是:底部导航用的控制器和导航图用的控制器是两个完全独立的实例,前者没有任何导航配置,自然无法执行跳转逻辑。
修复方案
我们需要让底部导航和导航图共享同一个NavHostController,同时修复选中状态的错误判断逻辑,具体修改如下:
1. 重构导航图组件,接收外部控制器
将NavigationGraph改为接收外部传入的NavHostController,不再内部创建:
@Composable fun NavigationGraph(navController: NavHostController) { NavHost( navController = navController, startDestination = BottomNavItem.Home.screen_route ){ composable(BottomNavItem.Home.screen_route) { HomeScreen() } composable(BottomNavItem.Call.screen_route) { CallScreen() } composable(BottomNavItem.Profile.screen_route) { ProfileScreen() } composable(BottomNavItem.Favorite.screen_route) { FavoriteScreen() } } }
2. 在DashboardScreen中共享控制器
创建一个统一的NavHostController,同时传递给底部导航和导航图:
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @OptIn(ExperimentalMaterial3Api::class) @Composable fun DashboardScreen(navController: NavHostController) { // 创建底部导航专用的共享控制器 val bottomNavController = rememberNavController() Scaffold( topBar = { CustomToolbarScreen(navController = navController, title = "Dashboard", false) } ) { innerPadding -> Column( modifier = Modifier.padding(innerPadding), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { Scaffold ( bottomBar = { BottomAppBar { // 传递共享控制器给底部导航 BottomNavigation(navController = bottomNavController) } } ) { scaffoldPadding -> Column(modifier = Modifier.padding(scaffoldPadding)) { // 传递同一个控制器给导航图 NavigationGraph(navController = bottomNavController) } } } } }
3. 修复底部导航组件,移除无效控制器
删掉BottomNavigation内部创建的NavHostController,改用传入的实例,并修复选中状态判断:
@Composable fun BottomNavigation(navController: NavHostController) { val items = listOf( BottomNavItem.Home, BottomNavItem.Call, BottomNavItem.Profile, BottomNavItem.Favorite ) val navBackStackEntry by navController.currentBackStackEntryAsState() val currentDestination = navBackStackEntry?.destination NavigationBar { items.forEach { item -> AddItem( screen = item, currentDestination = currentDestination, navController = navController ) } } } @Composable fun RowScope.AddItem( screen: BottomNavItem, currentDestination: NavDestination?, navController: NavHostController ) { // 用当前路由匹配判断是否选中,替换之前错误的类型匹配 val isSelected = currentDestination?.hierarchy?.any { it.route == screen.screen_route } == true NavigationBarItem( label = { Text(text = screen.title, fontSize = 9.sp) }, icon = { Icon(imageVector = screen.icon, contentDescription = screen.title) }, selected = isSelected, alwaysShowLabel = true, onClick = { navController.navigate(screen.screen_route) { navController.graph.findStartDestination().route?.let { startRoute -> popUpTo(startRoute) { saveState = true } } launchSingleTop = true restoreState = true } }, colors = NavigationBarItemDefaults.colors(), modifier = Modifier.background(colorPrimary) ) }
关键修改点说明
- 共享控制器:确保底部导航和导航图使用同一个
NavHostController实例,避免出现无配置的控制器。 - 修复选中判断:删除了无效的
selectedTab状态,改用导航控制器的当前路由来判断选中状态,逻辑更可靠且符合Jetpack Navigation的设计规范。 - 移除冗余代码:删掉了
BottomNavigation内部无用的NavHostController创建逻辑,以及注释掉的错误跳转代码。
内容的提问来源于stack exchange,提问作者Rahul Rokade
相关产品推荐
相关产品推荐

