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

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)
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不管认证状态都会尝试导航到该路由,未认证时导航图中找不到对应目的地,从而抛出异常。

修复步骤

  1. 统一路由常量
    确认Screens.GeoMarkerScreen的字符串值与BottomNavItem.Create.route完全一致(即"geoMarkerScreen"),避免拼写或大小写差异。

  2. 根据认证状态控制底部导航项显示
    修改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) }
            )
        }
    }
}
  1. 传递认证状态到BottomNavigationBar
    在AppNavigation的Scaffold中传入isAuthenticated参数:
Scaffold(
    bottomBar = {
        BottomNavigationBar(navController = navController, isAuthenticated = isAuthenticated)
    }
) { paddingValues ->
    // 修正:使用paddingValues避免布局遮挡
    NavHost(
        navController = navController,
        startDestination = Screens.SplashScreen,
        modifier = Modifier.padding(paddingValues)
    ) {
        // 原有导航项代码保持不变
    }
}
  1. 可选:未认证时拦截导航并跳转登录页
    如果不想隐藏按钮,可在点击时判断认证状态,未认证则导航到登录页:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 19:17:08