Hello guys! I am using the Type safe navigation an...
# compose
j
Hello guys! I am using the Type safe navigation and I want to pass a custom type argument during navigation but it keeps crashing the app with this error
java.lang.IllegalArgumentException: Cannot cast taxPayer of type com.nugitech.crptax.home.domain.model.TaxPayer to a NavType. Make sure to provide custom NavType for this argument.
i
So did you write a custom NavType and pass it in through your
typeMap
?
1
j
Thanks for the response.
However, I am using Jetpack compose
i
Yes, that's what uses the Navigation Kotlin DSL
1
j
I have read up the one on DSL and the example is only for primitive types
i
The example I linked to is a multiple field data class
Did you get a chance to read the blog post? It talks about how to hook one a custom NavType up to the Type Safe APIs: https://medium.com/androiddevelopers/navigation-compose-meet-type-safety-e081fb3cf2f8
j
This is what I did previously MY parcelable function `
Copy code
inline fun <reified T : Parcelable> parcelableType(
    isNullableAllowed: Boolean = false,
    json: Json = Json,
) = object : NavType<T>(isNullableAllowed = isNullableAllowed) {
    override fun get(bundle: Bundle, key: String) =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            bundle.getParcelable(key, T::class.java)
        } else {
            @Suppress("DEPRECATION")
            bundle.getParcelable(key)
        }

    override fun parseValue(value: String): T = json.decodeFromString(value)

    override fun serializeAsValue(value: T): String = json.encodeToString(value)

    override fun put(bundle: Bundle, key: String, value: T) = bundle.putParcelable(key, value)
}
My Navigation graph
Copy code
fun NavGraphBuilder.assessmentGraph(
    navigator: NavHostController, snackbar: SnackbarHostState
) {
    composable<AssessmentGraph.Assessment> (
        typeMap = mapOf(typeOf<TaxPayer>() to parcelableType<TaxPayer>())
    ){
        val route = it.toRoute<AssessmentGraph.Assessment>()
        AssessmentScreen(snackbar = snackbar, taxPayer = route.taxPayer) {

        }
    }
}
However, when I pass assessmentGraph to the Navhost, the app crashes with the error below
java.lang.IllegalArgumentException: No destination with route com.nugitech.crptax.home.presentation.screenGraphs.HomeGraph/{route} is on the NavController's back stack. The current destination is Destination(0xea762c48) route=com.nugitech.crptax.auth.presentation.screenGraphs.AuthGraph.SplashScreen
PS. When I remove the assessmentGraph, the app runs fine
i
That error message seems completely unrelated to your graph and completely different from the error message you started this conversation with. I'm afraid I'm not tracking
j
Yes. While on this, I bumped into the article you shared above and implemented the solution
That is what yielded this error
@Ian Lake Thanks for the help. I found my way around the issue. I really appreciate your help.
i
So what was the issue?
j
Unfortunately, It is not resolved. I am still having the same issue
I can't figure out what is that route
i
Seems like
SplashScreen
is trying to access
HomeGraph
j
Yes
The splash screen is supposed to navigate to HomeGraph after initializing the app. However, It keeps crashing but if i remove the route with the custom type, it works fine
i
That's not what the error says though. It says it is looking for HomeGraph on the back stack, which has nothing to do with a call to navigate? Maybe it would be helpful to include the full stack trace
j
Copy code
java.lang.IllegalArgumentException: No destination with route com.nugitech.crptax.home.presentation.screenGraphs.HomeGraph/{route} is on the NavController's back stack. The current destination is Destination(0xea762c48) route=com.nugitech.crptax.auth.presentation.screenGraphs.AuthGraph.SplashScreen
                                     	at androidx.navigation.NavController.getBackStackEntry(NavController.kt:2629)
                                     	at com.nugitech.crptax.home.presentation.screenGraphs.HomeGraphKt$homeGraph$1$1.invoke(HomeGraph.kt:76)
                                     	at com.nugitech.crptax.home.presentation.screenGraphs.HomeGraphKt$homeGraph$1$1.invoke(HomeGraph.kt:24)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:139)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                     	at androidx.navigation.compose.NavHostKt$NavHost$32$1.invoke(NavHost.kt:694)
                                     	at androidx.navigation.compose.NavHostKt$NavHost$32$1.invoke(NavHost.kt:693)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                     	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:401)
                                     	at androidx.compose.runtime.saveable.SaveableStateHolderImpl.SaveableStateProvider(SaveableStateHolder.kt:85)
                                     	at androidx.navigation.compose.NavBackStackEntryProviderKt.SaveableStateProvider(NavBackStackEntryProvider.kt:65)
                                     	at androidx.navigation.compose.NavBackStackEntryProviderKt.access$SaveableStateProvider(NavBackStackEntryProvider.kt:1)
                                     	at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:52)
                                     	at androidx.navigation.compose.NavBackStackEntryProviderKt$LocalOwnersProvider$1.invoke(NavBackStackEntryProvider.kt:51)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                     	at androidx.compose.runtime.CompositionLocalKt.CompositionLocalProvider(CompositionLocal.kt:380)
                                     	at androidx.navigation.compose.NavBackStackEntryProviderKt.LocalOwnersProvider(NavBackStackEntryProvider.kt:47)
                                     	at androidx.navigation.compose.NavHostKt$NavHost$32.invoke(NavHost.kt:693)
                                     	at androidx.navigation.compose.NavHostKt$NavHost$32.invoke(NavHost.kt:674)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:139)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                     	at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1$5.invoke(AnimatedContent.kt:803)
                                     	at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1$5.invoke(AnimatedContent.kt:792)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:118)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                     	at androidx.compose.animation.AnimatedVisibilityKt.AnimatedEnterExitImpl(AnimatedVisibility.kt:771)
                                     	at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1.invoke(AnimatedContent.kt:774)
                                     	at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1.invoke(AnimatedContent.kt:757)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:109)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:35)
                                     	at androidx.compose.animation.AnimatedContentKt.AnimatedContent(AnimatedContent.kt:816)
                                     	at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:646)
                                     	at androidx.navigation.compose.NavHostKt.NavHost(NavHost.kt:356)
                                     	at com.nugitech.crptax.MainActivity$onCreate$1$2$1$3.invoke(MainActivity.kt:217)
                                     	at com.nugitech.crptax.MainActivity$onCreate$1$2$1$3.invoke(MainActivity.kt:216)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl.invoke(ComposableLambda.jvm.kt:118) AndroidRuntime          	at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:130) (Ask Gemini)
                                     	at androidx.compose.runtime.internal.ComposableLambdaImpl$invoke$1.invoke(ComposableLambda.jvm.kt:129)
                                     	at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:192)
                                     	at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2823)
                                     	at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:3114)
                                     	at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3605)
                                     	at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:3550)
                                     	at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:948)
                                     	at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1198)
                                     	at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:132)
                                     	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:609)
                                     	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:578)
                                     	at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:41)
                                     	at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
                                     	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
                                     	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
                                     	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1229)
                                     	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
                                     	at android.view.Choreographer.doCallbacks(Choreographer.java:899)
                                     	at android.view.Choreographer.doFrame(Choreographer.java:827)
                                     	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
                                     	at android.os.Handler.handleCallback(Handler.java:942)
                                     	at android.os.Handler.dispatchMessage(Handler.java:99)
                                     	at android.os.Looper.loopOnce(Looper.java:201)
                                     	at android.os.Looper.loop(Looper.java:288)
                                     	at android.app.ActivityThread.main(ActivityThread.java:7872)
                                     	at java.lang.reflect.Method.invoke(Native Method)
                                     	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
                                     	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
                                     	Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@dca5b, androidx.compose.ui.platform.MotionDurationScaleImpl@6e912f8, StandaloneCoroutine{Cancelling}@8d4d3d1, AndroidUiDispatcher@fce0e36]
This is the navigation on the splash screen `
Copy code
LaunchedEffect(key1 = viewModel){
    viewModel.init().let{
        if(it){
            navigator.navigate(HomeGraph.Home){
                popUpTo(AuthGraph.SplashScreen){
                    inclusive = true
                }
            }
        }else{
            navigator.navigate(AuthGraph.LoginScreen){
                popUpTo(AuthGraph.SplashScreen){
                    inclusive = true
                }
            }
        }
    }
}
`
And my NavHost
Copy code
NavHost(
    modifier = Modifier.padding(innerPadding),
    navController = navigator,
    startDestination = AuthGraph.SplashScreen
) {
    assessmentGraph(navigator, snackbar)
    authNavigation(snackbar = snackbar, navigator = navigator)

    homeGraph(navigator, snackbar)
}
i
Your stack trace isn't pointing to your LaunchedEffect, it is pointing to a call to getBackStackEntry in composition
j
when I comment out this line
assessmentGraph(navigator, snackbar)
from the NavHost, everything works fine
i
Again, that's not what the stack trace says
j
That's where I am stuck. The issue started with the addition of
assessmentGraph(navigator, snackbar)
to the NavHost. If I remove it or change the parameter to primitive type, everything works fine
i
I'd suggest changing your approach to debugging entirely, since you are really getting distracted by red herrings. The error message tells you where you should be looking and it isn't in any of the code you've posted so far. Best of luck
1
j
Copy code
navigator.addOnDestinationChangedListener{controller, _, _ ->
    controller.currentBackStackEntry?.fromRoute()?.let {
        viewModel.setScreenRoute(it)
    }
}
This is where I suspect it is happening
Copy code
fun NavBackStackEntry?.fromRoute(): Screen {
    this?.destination?.route?.substringBefore("?")?.substringBefore("/")
        ?.substringAfterLast(".")?.let {
            return when (it) {
                AuthGraph.SplashScreen::class.simpleName -> AuthGraph.SplashScreen
                AuthGraph.LoginScreen::class.simpleName -> AuthGraph.LoginScreen
                HomeGraph::class.simpleName -> HomeGraph.Home
                HomeGraph.Home::class.simpleName -> HomeGraph.Home
                HomeGraph.History::class.simpleName -> HomeGraph.History
                AssessmentGraph.Assessment::class.simpleName ->  AssessmentGraph("Assessment")
                else ->  HomeGraph.Home
            }
        }
    return HomeGraph.Home
}
i
There's a lot wrong with that code (you should be using
currentBackStackEntryAsState
+
LaunchedEffect
and using
hasRoute
to figure out which route you are on), but that code has nothing to do with your stack trace, which is coming from the composition of
SplashScreen
j
Wow! Your suggestion worked. I still don't know what the correlation is but it solved the problem. Thanks a lot
d
Where can I find an example of how to add a value class to the Route.
684 Views