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

Jetpack Compose导航报错:无法匹配Recommendation/{recommendationId}目标

问题

我正尝试在应用中实现如下导航流程:

  • 展示分类列表供用户选择
  • 展示所选分类对应的推荐列表供用户选择
  • 用户点击推荐项后展示该推荐的详情

分类列表和推荐列表功能正常,但点击推荐项时应用崩溃,抛出异常:

java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/Recommendation/{recommendationId} } cannot be found in the navigation graph ComposeNavGraph(0x0) startDestination={Destination(0x6bf0b589) route=Categories}

相关代码:

sealed class MyCityScreen(val route: String) {
    object Categories: MyCityScreen("Categories")
    object Recommendations: MyCityScreen("Recommendations")
    object Recommendation: MyCityScreen("Recommendation/{recommendationId}")
}

@Composable
fun MyCityApp(
    windowSize: WindowWidthSizeClass,
    navController: NavHostController = rememberNavController(),
    modifier: Modifier = Modifier
) {
    val viewModel: MyCityViewModel = viewModel()
    val myCityUiState = viewModel.uiState.collectAsState().value

    val backStackEntry by navController.currentBackStackEntryAsState()

    val contentType: MyCityContentType = when (windowSize) {
        WindowWidthSizeClass.Compact -> {
            MyCityContentType.LIST_ONLY
        }
        WindowWidthSizeClass.Medium -> {
            MyCityContentType.LIST_ONLY
        }
        WindowWidthSizeClass.Expanded -> {
            MyCityContentType.LIST_AND_DETAIL
        }
        else -> {
            MyCityContentType.LIST_ONLY
        }
    }

    if (contentType != MyCityContentType.LIST_AND_DETAIL) {
        NavHost(
            navController = navController,
            startDestination = MyCityScreen.Categories.route
        ) {
            composable(route = MyCityScreen.Categories.route) {
                val categories = myCityUiState.categories

                LazyColumn(
                    modifier = Modifier
                        .padding(
                            end = dimensionResource(R.dimen.list_and_detail_list_padding_end),
                            top = dimensionResource(R.dimen.list_and_detail_list_padding_top)
                        ),
                    verticalArrangement = Arrangement.spacedBy(
                        dimensionResource(R.dimen.recommendation_list_padding)
                    )
                ) {
                    items(categories, key = { category -> category.id }) { category ->
                        MyCityCategoryListItem(
                            category = category,
                            selected = myCityUiState.currentSelectedRecommendation.id == category.id,
                            onCardClick = {
                                viewModel.updateRecommendations(category)
                                navController.navigate(MyCityScreen.Recommendations.route)
                            }
                        )
                    }
                }
            }

            composable(route = MyCityScreen.Recommendations.route) {
                val recommendations = myCityUiState.currentRecommendations

                LazyColumn(
                    modifier = Modifier
                        .padding(
                            end = dimensionResource(R.dimen.list_and_detail_list_padding_end),
                            top = dimensionResource(R.dimen.list_and_detail_list_padding_top)
                        ),
                    verticalArrangement = Arrangement.spacedBy(
                        dimensionResource(R.dimen.recommendation_list_padding)
                    )
                ) {
                    items(recommendations, key = {
                            recommendation -> recommendation.id
                    }) { recommendation ->
                        MyCityRecommendationListItem(
                            recommendation = recommendation,
                            selected =
                                myCityUiState.currentSelectedRecommendation.id == recommendation.id,
                            onCardClick = {
                                navController.navigate(
                                    MyCityScreen.Recommendation.route
                                )
                            }
                        )
                    }
                }
            }

            composable(
                route = MyCityScreen.Recommendation.route,
                arguments = listOf(navArgument("recommendationId") {
                    type = NavType.IntType
                })
            ) { backStackEntry ->
                val recommendationId = backStackEntry.arguments?.getInt("recommendationId")
                val recommendation =
                    myCityUiState.currentRecommendations[recommendationId!!]

                MyCityRecommendationDetailsCard(
                    recommendation = recommendation,
                    isFullScreen = false
                )
            }
        }
    } else {
        MyCityHomeScreen(
            contentType = contentType,
            myCityUiState = myCityUiState,
            onCategoryCardPressed = {
                viewModel.updateDetailsScreenStates(
                    category = myCityUiState.currentSelectedCategory,
                    recommendation = myCityUiState.currentSelectedRecommendation
                )
            },
            onDetailScreenBackPressed = {
                viewModel.resetHomeScreenStates()
            },
            onRecommendationCardPressed = {
                viewModel.updateDetailsScreenStates(
                    category = myCityUiState.currentSelectedCategory,
                    recommendation = myCityUiState.currentSelectedRecommendation
                )
            }
        )
    }
}

我已定义路由object Recommendation: MyCityScreen("Recommendation/{recommendationId}"),并在NavHost中配置了对应的composable节点,为何导航仍找不到该目标?


原因与解决方案

核心问题

你调用navController.navigate(MyCityScreen.Recommendation.route)时,直接使用了包含占位符{recommendationId}的原始路由字符串,没有替换为实际的ID值。Navigation组件无法识别带有未替换占位符的路由,因此找不到对应的目标节点。

修复步骤

  1. 修改导航调用代码
    在MyCityRecommendationListItem的onCardClick回调中,将占位符替换为当前推荐项的实际ID:

    onCardClick = {
        navController.navigate(
            MyCityScreen.Recommendation.route.replace("{recommendationId}", recommendation.id.toString())
        )
    }
    
  2. 推荐优化:类型安全的路由生成
    为避免手动替换占位符出错,可以给MyCityScreen.Recommendation添加一个生成带参数路由的方法:

    sealed class MyCityScreen(val route: String) {
        object Categories: MyCityScreen("Categories")
        object Recommendations: MyCityScreen("Recommendations")
        object Recommendation: MyCityScreen("Recommendation/{recommendationId}") {
            // 生成带实际ID的路由
            fun createRoute(recommendationId: Int) = "Recommendation/$recommendationId"
        }
    }
    

    导航时直接调用这个方法:

    onCardClick = {
        navController.navigate(MyCityScreen.Recommendation.createRoute(recommendation.id))
    }
    
  3. 额外优化:同步ViewModel状态
    点击推荐项时,建议同时更新ViewModel中currentSelectedRecommendation的状态,确保详情页能正确获取数据:

    onCardClick = {
        viewModel.updateSelectedRecommendation(recommendation)
        navController.navigate(MyCityScreen.Recommendation.createRoute(recommendation.id))
    }
    

修改后,Navigation组件就能正确识别导航目标,应用不会再崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 10:34:59