导航喷气背包撰写:非法参数异常:导航控制器的后堆栈上没有具有 ID 的目标



在我的应用程序中,我希望能够从不同的地方(从历史和从扫描仪)导航到CodeDetails屏幕。我制作了2个嵌套的导航图。因此,我可以成功地从History屏幕移动到CodeDetails屏幕,但是当我从Scanner屏幕移动时,我得到一个应用程序崩溃,并出现错误

. lang。在NavController的back stack上没有ID为-1689679215的目标。当前的目标是目的地(0 x4a873559)路线= code_details/{code_id} androidx.navigation.NavController.getBackStackEntry (NavController.kt: 2209) androidx.navigation.NavController.addEntryToBackStack (NavController.kt: 1918) androidx.navigation.NavController.addEntryToBackStack违约(NavController.kt: 1813)美元androidx.navigation.NavController导航4.美元调用(NavController.kt: 1721) androidx.navigation.NavController导航4.美元调用(NavController.kt: 1719)androidx.navigation.NavController NavControllerNavigatorState.push美元(NavController.kt: 287) androidx.navigation.NavigatorState.pushWithTransition (NavigatorState.kt: 88) androidx.navigation.compose.ComposeNavigator.navigate (ComposeNavigator.kt: 50) androidx.navigation.NavController.navigateInternal (NavController.kt: 260) androidx.navigation.NavController.navigate (NavController.kt: 1719) androidx.navigation.NavController.navigate (NavController.kt: 1662)androidx.navigation.NavController.navigate(NavController.kt:1984) at androidx.navigation.NavController.navigate$default(NavController.kt:1979) at com.t_ovchinnikova.android. scanroid_2 .ui. mainscreenkt $MainScreen$2$1$1.invoke(MainScreen.kt:31) at . com.t_ovchinnikova.android. scanroid_2 .ui.MainScreen $2$1$1$handleCode$1.invokeSuspend(ScanningViewModel.kt:67) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at android.os.Handler.handleCallback(Handler.java:883) at android.os.Handler.dispatchMessage(Handler.java:100) at android.os.Looper.loop(Looper.java:221) atandroid.app.ActivityThread.main(ActivityThread.java:7542) at java.lang.reflect.Method调用(本机方法)at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950) Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@f1fcaaf, Dispatchers.Main.immediate]

扫描仪图形:

fun NavGraphBuilder.scannerScreenNavGraph(
scannerScreenContent: @Composable () -> Unit,
codeDetailsScreenContent: @Composable (codeId: UUID) -> Unit
) {
navigation(
startDestination = Screen.Scanner.route,
route = Screen.ScannerMain.route
) {
composable(Screen.Scanner.route) {
scannerScreenContent()
}
composable(
route = Screen.CodeDetails.route,
arguments = listOf(
navArgument(KEY_CODE_ID) {
type = CodeNavigationType
}
)
) {
val codeId = it.arguments?.getSerializable(KEY_CODE_ID) as UUID?
?: throw RuntimeException("Args is null")
codeDetailsScreenContent(codeId)
}
}
}

历史图

fun NavGraphBuilder.historyScreenNavGraph(
historyScreenContent: @Composable () -> Unit,
codeDetailsScreenContent: @Composable (codeId: UUID) -> Unit
) {
navigation(
startDestination = Screen.History.route,
route = Screen.HistoryMain.route
) {
composable(Screen.History.route) {
historyScreenContent()
}
composable(
route = Screen.CodeDetails.route,
arguments = listOf(
navArgument(Screen.KEY_CODE_ID) {
type = CodeNavigationType
}
)
) {
val codeId = it.arguments?.getSerializable(Screen.KEY_CODE_ID) as UUID?
?: throw RuntimeException("Args is null")
codeDetailsScreenContent(codeId)
}
}
}

AppNavGraph

@Composable
fun AppNavGraph(
navHostController: NavHostController,
scannerScreenContent: @Composable () -> Unit,
codeDetailsScreenContent: @Composable (codeId: UUID) -> Unit,
historyScreenContent: @Composable () -> Unit,
settingsScreenContent: @Composable () -> Unit
) {
NavHost(
navController = navHostController,
startDestination = Screen.ScannerMain.route
) {
historyScreenNavGraph(
historyScreenContent,
codeDetailsScreenContent
)
scannerScreenNavGraph(
scannerScreenContent,
codeDetailsScreenContent
)
composable(Screen.Settings.route) {
settingsScreenContent()
}
}
}

屏幕

sealed class Screen(
val route: String
) {
object ScannerMain : Screen(ROUTE_SCANNER_MAIN)
object Scanner : Screen(ROUTE_SCANNER)
object CodeDetails : Screen(ROUTE_CODE_DETAILS) {
private const val ROUTE_FOR_ARGS = "code_details"
fun getRouteWithArgs(codeId: UUID): String {
return "$ROUTE_FOR_ARGS/$codeId"
}
}
object HistoryMain : Screen(ROUTE_HISTORY_MAIN)
object History : Screen(ROUTE_HISTORY)
object Settings : Screen(ROUTE_SETTINGS)
companion object {
const val KEY_CODE_ID = "code_id"
const val ROUTE_SCANNER_MAIN = "main"
const val ROUTE_SCANNER = "scanner"
const val ROUTE_CODE_DETAILS = "code_details/{$KEY_CODE_ID}"
const val ROUTE_HISTORY = "history"
const val ROUTE_HISTORY_MAIN = "history_main"
const val ROUTE_SETTINGS = "settings"
}
}
class NavigationState(
val navHostController: NavHostController
) {
fun navigateTo(route: String) {
navHostController.navigate(route) {
popUpTo(navHostController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
fun navigateToCodeDetails(codeId: UUID) {
navHostController.navigate(Screen.CodeDetails.getRouteWithArgs(codeId))
}
}

我MainScreen

@Composable
fun MainScreen() {
val navigationState = rememberNavigationState()
Scaffold(
bottomBar = {
BottomBar(navigationState)
}
) { paddingValues ->  
AppNavGraph(
navHostController = navigationState.navHostController,
scannerScreenContent = {
ScannerScreen(
paddingValues = paddingValues,
onScanListener = {
navigationState.navigateToCodeDetails(it)
}
)
},
codeDetailsScreenContent = {
CodeDetailsScreen(
codeId = it,
onBackPressed = {
navigationState.navHostController.popBackStack()
}
)
},
historyScreenContent = {
HistoryScreen(
codeItemClickListener = {
navigationState.navigateToCodeDetails(it)
}
)
},
settingsScreenContent = {
SettingsScreen()
}
)
}
}

如果我创建一个单独的类,用不同的路由和一个单独的方法导航到详细信息屏幕,那么一切都成功了。但是为什么我不能使用相同的路线从不同的地方到同一个屏幕呢?

@Composable
fun AppNavGraph(
navHostController: NavHostController,
scannerScreenContent: @Composable () -> Unit,
codeDetailsScreenContent: @Composable (codeId: UUID) -> Unit,
historyScreenContent: @Composable () -> Unit,
settingsScreenContent: @Composable () -> Unit
) {
NavHost(
navController = navHostController,
startDestination = Screen.ScannerMain.route
) {
scannerScreenNavGraph(
scannerScreenContent,
codeDetailsScreenContent
)
historyScreenNavGraph(
historyScreenContent,
codeDetailsScreenContent
)
composable(Screen.Settings.route) {
settingsScreenContent()
}
}
}
fun NavGraphBuilder.historyScreenNavGraph(
historyScreenContent: @Composable () -> Unit,
codeDetailsScreenContent: @Composable (codeId: UUID) -> Unit
) {
navigation(
startDestination = Screen.History.route,
route = Screen.HistoryMain.route
) {
composable(Screen.History.route) {
historyScreenContent()
}
composable(
route = Screen.HistoryCodeDetails.route,
arguments = listOf(
navArgument(Screen.KEY_CODE_ID) {
type = CodeNavigationType
}
)
) {
val codeId = it.arguments?.getSerializable(Screen.KEY_CODE_ID) as UUID?
?: throw RuntimeException("Args is null")
codeDetailsScreenContent(codeId)
}
}
}
@Composable
fun MainScreen() {
...
historyScreenContent = {
HistoryScreen(
codeItemClickListener = {
navigationState.navigateToHistoryCodeDetails(it)
}
)
},
...
}
class NavigationState(
val navHostController: NavHostController
) {
fun navigateTo(route: String) {
navHostController.navigate(route) {
popUpTo(navHostController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
fun navigateToCodeDetails(codeId: UUID) {
navHostController.navigate(Screen.CodeDetails.getRouteWithArgs(codeId))
}
fun navigateToHistoryCodeDetails(codeId: UUID) {
navHostController.navigate(Screen.HistoryCodeDetails.getRouteWithArgs(codeId))
}
}
sealed class Screen(
val route: String
) {
object ScannerMain : Screen(ROUTE_SCANNER_MAIN)
object Scanner : Screen(ROUTE_SCANNER)
object CodeDetails : Screen(ROUTE_CODE_DETAILS) {
private const val ROUTE_FOR_ARGS = "code_details"
fun getRouteWithArgs(codeId: UUID): String {
return "$ROUTE_FOR_ARGS/$codeId"
}
}
object HistoryCodeDetails : Screen(ROUTE_CODE_INFO_FROM_HISTORY) {
private const val ROUTE_FOR_ARGS = "history_code_details"
fun getRouteWithArgs(codeId: UUID): String {
return "$ROUTE_FOR_ARGS/$codeId"
}
}
object HistoryMain : Screen(ROUTE_HISTORY_MAIN)
object History : Screen(ROUTE_HISTORY)
object Settings : Screen(ROUTE_SETTINGS)
companion object {
const val KEY_CODE_ID = "code_id"
const val ROUTE_SCANNER_MAIN = "main"
const val ROUTE_SCANNER = "scanner"
const val ROUTE_CODE_DETAILS = "code_details/{$KEY_CODE_ID}"
const val ROUTE_CODE_INFO_FROM_HISTORY = "history_code_details/{$KEY_CODE_ID}"
const val ROUTE_HISTORY = "history"
const val ROUTE_HISTORY_MAIN = "history_main"
const val ROUTE_SETTINGS = "settings"
}
}

我建议您将路由名称更改为

const val ROUTE_SCANNER_MAIN = "main"
const val ROUTE_SCANNER = "scanner"
const val ROUTE_CODE_DETAILS = "code_details"
const val ROUTE_CODE_INFO_FROM_HISTORY = "history_code_details"
const val ROUTE_HISTORY = "history"
const val ROUTE_HISTORY_MAIN = "history_main"
const val ROUTE_SETTINGS = "settings"

并使用这些作为路径。例如,

object CodeDetails : Screen(ROUTE_CODE_DETAILS) {
private const val ROUTE_FOR_ARGS = "code_details"
fun getRouteWithArgs(codeId: UUID): String {
return "$ROUTE_FOR_ARGS/$codeId"
}
}

这部分应该改成

object CodeDetails : Screen(ROUTE_CODE_DETAILS) {
fun getRouteWithArgs(codeId: UUID): String {
return "$ROUTE_CODE_DETAILS/$codeId"
}
}

对于图之间的传递数据,最好将该数据保存在全局状态数据中并重用。

否则,您可以检查此源代码

下面是文件中的一小段:

navigation(startDestination = Routes.DESTINATION_PROFILE, route="${Routes.NAVIGATION_LOGGED_IN}/{${Routes.NAVIGATION_LOGGED_IN_ARG_USERNAME}}") {
composable(route = Routes.DESTINATION_PROFILE) { navBackStackEntry ->
val profileViewModel: ProfileViewModel = hiltViewModel(viewModelStoreOwner = navBackStackEntry)
val username = navBackStackEntry.arguments!!.getString("username")!!
ProfileScreen(profileViewModel, username)
}
}

最新更新