尚未为 NavController compose 设置导航图

问题描述 投票:0回答:1

这是代码。似乎一切都设置正确,但我似乎无法弄清楚为什么这不起作用。单击列表项后,我从 1 个主屏幕导航到详细信息屏幕。如果需要,我可以提供更多详细信息。


@Composable
fun Navigation(navController: NavHostController, viewModel: GamesViewModel){
    NavHost(navController = navController, startDestination = Screen.MainScreen.route){
        composable(route = Screen.MainScreen.route) {
            OverallStanding(viewModel, navController)
        }

        navigation(startDestination = Screen.MainScreen.route, route = Screen.DetailsScreen.route){
            composable(route = Screen.DetailsScreen.route + "/{teamName}",
                arguments = listOf(navArgument("teamName"){
                    NavType.StringType
                    nullable = false
                })
            ) { entry ->
                GamesDetailScreen(viewModel, navController, teamName = entry.arguments?.getString("teamName"))
            }
        }
    }
}

这是我调用函数的可组合项

@Composable
fun TeamStatsItem(teamStatsItem: TeamStats, isMainScreen: Boolean) {
    val navController = rememberNavController()
    val lastColumnValue =
        if (isMainScreen) teamStatsItem.winPercentage.toString() + "%" else teamStatsItem.totalGamesPlayed.toString()
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clip(RoundedCornerShape(16.dp))
            .padding(12.dp)
            .height(55.dp)
            .background(Color.White)
            .clickable(
                true,
                onClick = { navController.navigate(Screen.DetailsScreen.withArguments(teamStatsItem.teamName)) }),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(text = teamStatsItem.teamName, modifier = Modifier.width(175.dp))
        Text(text = teamStatsItem.wins.toString(), modifier = Modifier.padding(end = 42.dp))
        Text(text = teamStatsItem.losses.toString(), modifier = Modifier.padding(end = 42.dp))
        Text(text = teamStatsItem.draws.toString(), modifier = Modifier.padding(end = 42.dp))
        Text(text = lastColumnValue)
    }
}

这是我收到的完整错误。正是通过这个函数调用来在上面的可组合项中导航。

FATAL EXCEPTION: main
                                                                                                    Process: com.dushanesmith.yahoocodingexercise, PID: 29195
                                                                                                    java.lang.IllegalArgumentException: Cannot navigate to details_screen/Olympiacos. Navigation graph has not been set for NavController androidx.navigation.NavHostController@79d6b61.
                                                                                                        at androidx.navigation.NavController.navigate(NavController.kt:2375)
                                                                                                        at androidx.navigation.NavController.navigate$default(NavController.kt:2370)
                                                                                                        at com.dushanesmith.yahoocodingexercise.TeamStatsItemKt.TeamStatsItem$lambda$0(TeamStatsItem.kt:35)
                                                                                                        at com.dushanesmith.yahoocodingexercise.TeamStatsItemKt.$r8$lambda$TBskDp7ealVso3TrsxH8j1ss2Nw(Unknown Source:0)
                                                                                                        at com.dushanesmith.yahoocodingexercise.TeamStatsItemKt$$ExternalSyntheticLambda0.invoke(D8$$SyntheticClass:0)
                                                                                                        at androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke-k-4lQ0M(Clickable.kt:639)
                                                                                                        at androidx.compose.foundation.ClickableNode$clickPointerInput$3.invoke(Clickable.kt:633)
                                                                                                        at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapAndPress$2$1.invokeSuspend(TapGestureDetector.kt:255)
                                                                                                        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
                                                                                                        at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:179)
                                                                                                        at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:168)
                                                                                                        at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474)
                                                                                                        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508)
                                                                                                        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497)
                                                                                                        at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:368)
                                                                                                        at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl$PointerEventHandlerCoroutine.offerPointerEvent(SuspendingPointerInputFilter.kt:719)
                                                                                                        at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.dispatchPointerEvent(SuspendingPointerInputFilter.kt:598)
                                                                                                        at androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNodeImpl.onPointerEvent-H0pRuoY(SuspendingPointerInputFilter.kt:620)
                                                                                                        at androidx.compose.foundation.AbstractClickableNode.onPointerEvent-H0pRuoY(Clickable.kt:1044)
                                                                                                        at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:387)
                                                                                                        at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:373)
                                                                                                        at androidx.compose.ui.input.pointer.Node.dispatchMainEventPass(HitPathTracker.kt:373)
                                                                                                        at androidx.compose.ui.input.pointer.NodeParent.dispatchMainEventPass(HitPathTracker.kt:229)
                                                                                                        at androidx.compose.ui.input.pointer.HitPathTracker.dispatchChanges(HitPathTracker.kt:144)
                                                                                                        at androidx.compose.ui.input.pointer.PointerInputEventProcessor.process-BIzXfog(PointerInputEventProcessor.kt:120)
                                                                                                        at androidx.compose.ui.platform.AndroidComposeView.sendMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1999)
                                                                                                        at androidx.compose.ui.platform.AndroidComposeView.handleMotionEvent-8iAsVTc(AndroidComposeView.android.kt:1950)
                                                                                                        at androidx.compose.ui.platform.AndroidComposeView.dispatchTouchEvent(AndroidComposeView.android.kt:1834)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
                                                                                                        at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
                                                                                                        at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2801)
                                                                                                        at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:490)
                                                                                                        at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1904)
                                                                                                        at android.app.Activity.dispatchTouchEvent(Activity.java:4377)
                                                                                                        at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:448)
2024-10-17 23:53:04.058 29195-29195 AndroidRuntime          com...hanesmith.yahoocodingexercise  E      at android.view.View.dispatchPointerEvent(View.java:15919)
                                                                                                        at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:7021)
                                                                                                        at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:6815)
                                                                                                        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6229)
                                                                                                        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6286)
                                                                                                        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6252)
                                                                                                        at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:6417)
                                                                                                        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6260)
                                                                                                        at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:6474)
                                                                                                        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6233)
                                                                                                        at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:6286)
                                                                                                        at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:6252)
                                                                                                        at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:6260)
                                                                                                        at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:6233)
                                                                                                        at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:9211)
                                                                                                        at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:9162)
                                                                                                        at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:9131)
                                                                                                        at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:9337)
                                                                                                        at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:267)
                                                                                                        at android.os.MessageQueue.nativePollOnce(Native Method)
                                                                                                        at android.os.MessageQueue.next(MessageQueue.java:335)
                                                                                                        at android.os.Looper.loopOnce(Looper.java:162)
                                                                                                        at android.os.Looper.loop(Looper.java:294)
                                                                                                        at android.app.ActivityThread.main(ActivityThread.java:8177)
                                                                                                        at java.lang.reflect.Method.invoke(Native Method)
                                                                                                        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
                                                                                                        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
                                                                                                        Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.ui.platform.MotionDurationScaleImpl@41684e3, androidx.compose.runtime.BroadcastFrameClock@4a5eee0, StandaloneCoroutine{Cancelling}@aaf2599, AndroidUiDispatcher@75dd05e]

我尝试更改定义导航图的方法。

android kotlin debugging android-jetpack-compose jetpack-compose-navigation
1个回答
0
投票

您的

TeamStatsItem
正在使用:

val navController = rememberNavController()

这将创建一个全新的 NavController,您从未将其与

NavHost
关联,因此 that NavController 上没有设置任何导航图,就像错误消息所说的那样。

您需要使用您传递给

NavHost
的确切 NavController。

虽然您可以像对

OverallStanding
GamesDetailScreen
可组合项那样通过可组合项传递它,但这不是最佳实践。根据 Navigation Compose 测试指南

将导航代码与可组合目标分离,以便能够独立于 NavHost 可组合项单独测试每个可组合项。

这意味着您不应将

navController
直接传递到任何可组合项中,而应将导航回调作为参数传递。这允许您的所有可组合项都可以单独测试,因为它们在测试中不需要
navController
的实例。

可组合 lambda 提供的间接级别可让您将导航代码与可组合本身分开。这有两个方向:

  1. 仅将解析后的参数传递到可组合项中
  2. 传递应由可组合项触发的 lambda 进行导航,而不是
    NavController
    本身。

因此,您可以将可组合项编写为:

@Composable
fun TeamStatsItem(
    teamStatsItem: TeamStats,
    isMainScreen: Boolean,
    onTeamSelected: (teamName: String) -> Unit
) {
    val lastColumnValue =
        if (isMainScreen) teamStatsItem.winPercentage.toString() + "%" else teamStatsItem.totalGamesPlayed.toString()
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clip(RoundedCornerShape(16.dp))
            .padding(12.dp)
            .height(55.dp)
            .background(Color.White)
            .clickable(
                true,
                onClick = { onTeamSelected(teamStatsItem.teamName) }),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Text(text = teamStatsItem.teamName, modifier = Modifier.width(175.dp))
        Text(text = teamStatsItem.wins.toString(), modifier = Modifier.padding(end = 42.dp))
        Text(text = teamStatsItem.losses.toString(), modifier = Modifier.padding(end = 42.dp))
        Text(text = teamStatsItem.draws.toString(), modifier = Modifier.padding(end = 42.dp))
        Text(text = lastColumnValue)
    }
}

这样您就可以轻松地为此可组合项编写

@Preview
,并为其单独编写单元测试。

调用它的可组合项将这样调用它:

TeamStatsItem(
    teamStatsItem,
    isMainScreen
) { teamName ->
    navController.navigate(Screen.DetailsScreen.withArguments(teamName)
}

或者将 lambda 传递到另一层,理想情况下一直到您的

NavHost
级别(这应该是唯一真正可以访问您的
NavController
的层)。

© www.soinside.com 2019 - 2024. All rights reserved.