I’ve added nested navigation to my app and everyth...
# compose
j
I’ve added nested navigation to my app and everything is working great except this one scenario. We have a need to open internal deeplinks from a Uri that we get from our backend. The destination for this deeplink is defined within a nav graph in our nav host. When we open a deeplink from outside the app, the backstack looks like this:
root > destination nav graph (ng1) > Start destination (A) > deeplink destination (B)
. When I use
navController.navigation(deeplink=uri)
with navOptions to popUpTo the root nav graph,
A
is not inserted into the backstack behind
B
like it is with the external deeplinks.
👀 1
Is there some combination of NavOptions or Navigator.Extras that I can use to get the start destination to be injected?
I also tried using a PendingIntent which does work like I expect except it restarts the activity and shows the Splash screen very quickly
m
Following this tread because I want to know the answer too
(my junior spirit would probably just handle deep link launchers with a different set of logic)
j
I think, maybe, I could solve this by making A it’s own nav graph and then making B a nested destination within it, but I’m not sure if I can make a nav graph the starting destination of another nav graph
m
hmm maybe you could check if nav A was opened by a deep link. If so, create a new stack of navigation? I'm just guessing
I can see how this doesn't scale well in multi module projects, when one module doesn't know about others
j
I’ve also tried creating an Intent with the deeplink uri as the data and calling
navController.handleDeepLink(intent)
and this gets the backstack in the state I expect, but it still shows the splash screen
yeah, I’d like to avoid examining the backstack and doing like manual two step navigations or something like that
d
i don't know the answer to your specific question, but have you read this https://developer.android.com/jetpack/compose/navigation#deeplinks
j
yeah
it suggests using pendingIntent which shows the splash screen in this case which I don’t want
it appears the behavior of
navController.navigate(deeplink = uri)
is as designed: https://issuetracker.google.com/issues/154760276
which makes sense
I guess my question becomes: Is there a way to navigate to the deeplink with a PendingIntent without showing the splash screen?
m
maybe splash screen is showing because the app hasn't been loaded to memory yet?
j
nah. I’m opening this link from within the app. So the app is definitely in memory
d
Isn't that launching a new activity just hence why it works but you see the splashscreen? Edit: actually you still shouldn't see the splashscreen so I'm wrong I think this needs to be "fixed" on nav graph/component level but if you really wanna go that route you probably need to look at launchMode and onNewIntent (imo this approach is bad and you don't want to be launching new activities at all)
j
Yeah, I definitely don't want to launch a new activity but it seems to be the only way I can get the nav controller to fill in the start destination of my graph for me
I may have to just back fill it myself
d
From the user's point of view, what should happen when they press back on your destination screen, where do they land?
j
I want them to land on destination A when they go back from destination B.
d
Would that mean that the user is brought "back" to a screen they never visibly visited? Perhaps nav component is trying to guard against such UX exactly?
j
It does mean that they could be brought back to a screen they haven't visited yet but the navigation docs specifically state that artificially filling in the start destination of a graph on the backstack when navigating to a deeplink is supported and it works that way if the same link is opened from outside the app.
So I was trying to replicate that behavior in this situation.
d
Just from memory of the old fragment safe navigation plugin - I think it wouldn't let you navigate to a destination that is on a different nav graph, other than the start destination of said other graph Needs to be double checked, I might be wrong about this
j
I was able to make this work how I needed to by doing the following with my deeplink URI inside my app:
Copy code
val deepLinkMatch = navController.graph.matchDeepLink(NavDeepLinkRequest(deepLinkUri, null, null))
                if (deepLinkMatch != null) {
                    val graphStartRoute = deepLinkMatch.destination.parent?.startDestinationRoute
                    val deeplinkMatchRoute = deepLinkMatch.destination.route
                    var backstackPopped = false

                    // If the deeplink is not the start of its graph, we need to put the start destination on the backstack
                    // This only handles a single level of nav graph nesting so a nav graph nested
                    // within a nav graph may not work as intended.
                    if (graphStartRoute != null && graphStartRoute != deeplinkMatchRoute) {
                        navController.navigate(
                            graphStartRoute,
                            navOptions {
                                popUpTo(RootNavGraph.route)
                            }
                        )
                        backstackPopped = true
                    }
                    navController.navigate(
                        deepLinkUri,
                        navOptions {
                            launchSingleTop = true
                            if (!backstackPopped) {
                                popUpTo(RootNavGraph.route)
                            }
                        }
                    )
only handles a single level of nested nav graphs, but that is good enough for my needs right now.
512 Views