raamcosta / compose-destinations

Annotation processing library for type-safe Jetpack Compose navigation with no boilerplate.
https://composedestinations.rafaelcosta.xyz
Apache License 2.0
3.22k stars 133 forks source link

Deeplink cannot be handled as navGraph is null #684

Closed lucanicoletti closed 3 weeks ago

lucanicoletti commented 1 month ago

In my app I have the following setup

val engine = rememberAnimatedNavHostEngine()
val navController = engine.rememberNavController()
val sheetState = initModalBottomSheetState()
val bottomSheetNavigator = remember { BottomSheetNavigator(sheetState) }
navController.navigatorProvider += bottomSheetNavigator

ModalBottomSheetLayout(
    sheetState = sheetState,
    sheetContent = bottomSheetNavigator.sheetContent,
    sheetShape = RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp)
) {
    DestinationsNavHost(
        navGraph = NavGraphs.root,
        startRoute = startRoute, // defined with a condition, but it is a `@Destination`
        engine = engine,
        navController = navController
    )
}

// code added recently for deeplinking
val deeplink = intent.getStringExtra("deeplink")
deeplink?.let { link ->
    val otpScheme = stringResource(id = R.string.otp_scheme)
    val correctedLink = link.replace(otpScheme, "app") // work-around to avoid `BuildConfig` to be used in `deepLink`
    navController.navigate(correctedLink.toUri())
}

With this setup though, the .navigate throws an exception:

Cannot navigate to NavDeepLinkRequest{ uri=app://path }. Navigation graph has not been set for NavController androidx.navigation.NavHostController@7ab25eb.

Any idea of when/how I could prevent this from happening? Thanks.

lucanicoletti commented 1 month ago

As an update: I took the change to refactor to v2. The code now looks like this:

ModalNavigationDrawer(
    drawerContent = {
        SideMenu(destinationsNavigator = destinatorNavigator)
    },
    drawerState = drawerState,
    gesturesEnabled = rememberIsDrawerOpen(drawerState),
    scrimColor = Color.Black.withAlpha(LOW_ALPHA),
) {
    val snackbarHostState = remember { SnackbarHostState() }
    HandlesSnackBar(snackbarHostState)
    Scaffold(
        bottomBar = { SitkaBottomBar(navController, onScreenButtonClick(destinatorNavigator)) },
        snackbarHost = { SitkaSnackbarHost(snackbarHostState = snackbarHostState) }
    ) { paddingValues ->
        DestinationsNavHost(
            engine = engine,
            navGraph = NavGraphs.root,
            start = startRoute,
            navController = navController,
            modifier = Modifier
                .padding(paddingValues)
                .background(MaterialTheme.colorScheme.surface)
                .fillMaxSize(),
            dependenciesContainerBuilder = {
                dependency { openMenu() }
                dependency(::navigateToScreen)
            }
        )
    }
}
if (deeplink.isNotBlank()) {
    navController.navigate(deeplink)
}

The error is still the same, the NavController graph has not been set.

raamcosta commented 3 weeks ago

Hey 👋

Doesn't seem related with Compose Destinations. I believe when NavHost gets called, it internally sets the graph to the NavController passed in. So are you sure that you're not calling navController.navigate before DestinationsNavHost gets called?

lucanicoletti commented 3 weeks ago

@raamcosta as you can see from the code, the navController.navigate is done after the DestinationsNavHost invocation. I thought it was a problem of the underneath Android SDK as googling the error I've seen it happens to other people as well, but unfortunately all the solutions I found didn't work in my scenario.

raamcosta commented 3 weeks ago

Can you log before each call and test? I don’t think it’s that easy because as you can see the DestinationsNavHost is inside a lambda.

lucanicoletti commented 3 weeks ago

Indeed the navigate happens before. Any way to attach a callback to the DestinationsNavHost?

raamcosta commented 3 weeks ago

If this is inside a composable, it should also be inside some Effect API, and probably after DestinationsNavHost but within the same lambda.

I’ll close this issue as it’s not compose destinations related. Hope that helps!