Jetpack Compose导航报错:找不到geoMarkerScreen导航目标的解决求助
问题描述
使用Jetpack Compose Navigation组件从HomeScreen导航到GeoMarkerScreen时触发IllegalArgumentException,报错信息:
IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest-geoMarkerScreen can't be found in the navigation graph
问题出在BottomNavigationBar中,相关代码如下:
BottomNavigationBar.kt
package com.example.bettehomes.navigation import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.navigation.NavController import androidx.navigation.compose.currentBackStackEntryAsState @Composable fun BottomNavigationBar(navController: NavController) { NavigationBar { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route BottomNavItem.values().forEach { item -> NavigationBarItem(selected = currentRoute == item.route, onClick = { navController.navigate(item.route) { popUpTo(navController.graph.startDestinationId) { saveState = true } launchSingleTop = true restoreState = true } }, icon = { Icon(item.icon, contentDescription = null) },label = { Text(item.label) }) } } }
错误发生位置:
com.example.bettehomes.navigation.BottomNavigationBarKt$BottomNavigationBar$1$1$1.invoke(BottomNavigationBar.kt:23) at com.example.bettehomes.navigation.BottomNavigationBarKt$BottomNavigationBar$1$1$1.invoke(BottomNavigationBar.kt:21)
NavHost.kt
package com.example.bettehomes.navigation import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import com.example.bettehomes.data.login.AuthViewModel import com.example.bettehomes.presentation.GeoMarkerViewModel import com.example.bettehomes.presentation.authscreens.HomeScreen import com.example.bettehomes.presentation.authscreens.LoginScreen import com.example.bettehomes.presentation.authscreens.PrivacyPolicyScreen import com.example.bettehomes.presentation.authscreens.SignUpScreen import com.example.bettehomes.presentation.authscreens.TermsAndConditionsScreen import com.example.bettehomes.presentation.authscreens.settings.SettingsScreen import com.example.bettehomes.presentation.authscreens.splash.SplashScreen import com.example.bettehomes.presentation.mapscreens.GeoMarkerScreen import com.example.bettehomes.presentation.mapscreens.MapsScreen @OptIn(ExperimentalMaterial3Api::class) @Composable fun AppNavigation( snackbarHostState: SnackbarHostState, geoMarkerViewModel: GeoMarkerViewModel, fetchLocationUpdates: () -> Unit, authViewModel: AuthViewModel = hiltViewModel() ) { val navController = rememberNavController() val isAuthenticated by authViewModel.isAuthenticated.collectAsState() Scaffold( bottomBar = { BottomNavigationBar(navController = navController) } ) {it NavHost(navController = navController, startDestination = Screens.SplashScreen) { composable(Screens.SplashScreen) { SplashScreen(openAndPopUp = { route, popUp -> navController.navigateAndPopUp(route, popUp) }) } composable(Screens.HomeScreen) { HomeScreen() } composable(Screens.MapScreen) { MapsScreen( snackbarHostState = snackbarHostState, navController = navController, fetchLocationUpdates = fetchLocationUpdates ) } composable(Screens.SignUpScreen) { SignUpScreen(navController) } composable(Screens.LoginScreen) { LoginScreen(navController) } composable(Screens.TermsAndConditions) { TermsAndConditionsScreen(navController) } composable(Screens.PrivacyPolicy) { PrivacyPolicyScreen(navController) } if (isAuthenticated) { composable(Screens.GeoMarkerScreen) { GeoMarkerScreen(geoMarkerViewModel, navController) } } else { composable(Screens.LoginScreen) { LoginScreen(navController) } } composable(Screens.SettingsScreen) { SettingsScreen( restartApp = { route -> navController.clearAndNavigate( route, startDestination = 1 ) }, openScreen = { route -> navController.navigate(route) } ) } } } }
BottomNavItem.kt
package com.example.bettehomes.navigation import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Home import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Settings import androidx.compose.ui.graphics.vector.ImageVector enum class BottomNavItem(val route: String, val icon: ImageVector, val label: String) { Home("homeScreen", Icons.Default.Home, "Home"), Create("geoMarkerScreen", Icons.Default.Add, "Create"), Search("mapsScreen", Icons.Default.Search, "Search"), Settings("settingsScreen", Icons.Default.Settings, "Settings"); }
解决方案
错误核心原因:GeoMarkerScreen的路由仅在用户已认证时才添加到导航图,但BottomNavigationBar不管认证状态都会尝试导航到该路由,未认证时导航图中找不到对应目的地,从而抛出异常。
修复步骤
统一路由常量
确认Screens.GeoMarkerScreen的字符串值与BottomNavItem.Create.route完全一致(即"geoMarkerScreen"),避免拼写或大小写差异。根据认证状态控制底部导航项显示
修改BottomNavigationBar,接收认证状态参数,过滤掉未认证时不可访问的项:
@Composable fun BottomNavigationBar(navController: NavController, isAuthenticated: Boolean) { NavigationBar { val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route // 未认证时隐藏Create项 val displayItems = if (isAuthenticated) { BottomNavItem.values().toList() } else { BottomNavItem.values().filterNot { it == BottomNavItem.Create } } displayItems.forEach { item -> NavigationBarItem( selected = currentRoute == item.route, onClick = { navController.navigate(item.route) { popUpTo(navController.graph.startDestinationId) { saveState = true } launchSingleTop = true restoreState = true } }, icon = { Icon(item.icon, contentDescription = item.label) }, label = { Text(item.label) } ) } } }
- 传递认证状态到BottomNavigationBar
在AppNavigation的Scaffold中传入isAuthenticated参数:
Scaffold( bottomBar = { BottomNavigationBar(navController = navController, isAuthenticated = isAuthenticated) } ) { paddingValues -> // 修正:使用paddingValues避免布局遮挡 NavHost( navController = navController, startDestination = Screens.SplashScreen, modifier = Modifier.padding(paddingValues) ) { // 原有导航项代码保持不变 } }
- 可选:未认证时拦截导航并跳转登录页
如果不想隐藏按钮,可在点击时判断认证状态,未认证则导航到登录页:
onClick = { if (item == BottomNavItem.Create && !isAuthenticated) { navController.navigate(Screens.LoginScreen) } else { navController.navigate(item.route) { popUpTo(navController.graph.startDestinationId) { saveState = true } launchSingleTop = true restoreState = true } } }
额外检查
确保isAuthenticated状态更新时,导航图会重新组合,正确添加/移除GeoMarkerScreen导航项。
内容的提问来源于stack exchange,提问作者Kaleab Woldemariam
相关产品推荐
相关产品推荐

