hey! About navigation component… I can’t use popUp...
# compose
j
hey! About navigation component… I can’t use popUpTo with a String (route) in nav-alpha06/compose-alpha11. If I write popUpTo AS shows both options (the one that accepts id:Int and the one that accepts route:String… but then it only accepts the Int version. And in the NavOptionsBuilder there is only the id:Int version, which makes sense with AS not compiling with a String… How do I use popUpTo with the route?
k
You’d have to manually import it. Android studio imports the wrong one.
j
I thought so, but was not able to find it. How can I find its import?
k
Wait are you talking about the
navigate()
function?
i’m not seeing any
popUpTo
that supports a
string
j
navController.navigate(path) { popUpTo }
k
and the variable
path
is in question correct?
you want to pass
path
as a string rather than
int
?
j
I already pass path as String and it works fine. But then in popUpTo I would also like to pass a String and am only able to pass an Int
k
I dont think you can pass a
String
. In my opinion, it only takes a @IdRes
Int
value.
im using alpha11 as well. unless i’m missing something.
What you can do is reach into the graph itself like below:
Copy code
navController.navigate(bottomNavigationitem.route) {
                                    popUpTo = navController.graph.startDestination
                                    launchSingleTop = true
                                }
j
in the (old) docs it shows how to pass a string. But more importantly, if all our destinations are build with strings in compose, it makes sense that we would pass a String, doesn’t it?
With the graph I do not see how to find a non-start destination to fetch its id
k
graph does have it function in its definition to search via deeplink:
Copy code
DeepLinkMatch matchDeepLink(@NonNull NavDeepLinkRequest request) {
        // First search through any deep links directly added to this NavGraph
        DeepLinkMatch bestMatch = super.matchDeepLink(request);
        // Then search through all child destinations for a matching deep link
        for (NavDestination child : this) {
            DeepLinkMatch childBestMatch = child.matchDeepLink(request);
            if (childBestMatch != null && (bestMatch == null
                    || childBestMatch.compareTo(bestMatch) > 0)) {
                bestMatch = childBestMatch;
            }
        }
        return bestMatch;
    }
But let me see why
string
cant be passed
Ahh this is what that
String
version does:
Copy code
fun NavOptionsBuilder.popUpTo(route: String, popUpToBuilder: PopUpToBuilder.() -> Unit) {
    popUpTo(createRoute(route).hashCode(), popUpToBuilder)
}
createRoute
simply appends the
route
and returns the string
Copy code
internal fun createRoute(route: String) = "<android-app://androidx.navigation.compose/$route>"
which then gets converted to an Int via
hashCode
j
humm ok. But it does not make sense that we have to do that manually… There must be a way to get the conversion, mustn’t it?
👍 1
k
@Ian Lake i’m not quite sure what’s happening here 😅
Copy code
internal fun createRoute(route: String) = "<android-app://androidx.navigation.compose/$route>"
either the
popUpTo
function that takes a
String
argument and a
lambda
is deprecated or somehow the
navbackStack
assigned this randomly generated
Int
via
hashCode()
to the respective
composable
entry?
@Jordi Saumell
popUpTo
always takes an
Int
. Even the
String
is being converted to an
Int
. The question is, how does this
String
gets matched to the correct
composable
.
i
if you have
import androidx.navigation.compose.popUpTo
, you can pass a route to
popUpTo
, not just an Int. That route needs to exactly match the
route
you've used when constructing your
composable
in your
NavHost
That's the whole reason the extension function exists 🙂
k
But that extension function converts this
String
into an
Int
no? via
Copy code
createRoute(route: String) = "<android-app://androidx.navigation.compose/$route>"
i
That's an implementation detail on how routes work with Navigation Compose right now, yes
k
Wait i get it, it just clicked 🤦‍♂️ . The
Int
after hashing would point to that destination.
i
Yep, you shouldn't ever have to think about that mapping - the extension functions we provide allow you to work solely in the route land
j
thank you @Ian Lake! and since we are at it why do I need to provide the popUpToBuilder? I see it is used for setting inclusive (not sure if anything else, but if the default to false is Ok..?
k
Gotcha. I was wondering how that
Int
generated via hash would match correctly. But that’s an implementation detail.
j
oh and also, how do I find those extensions when AS doesn’t autocomplete them? in this case I could even see it and press intro on it, but it wouldn’t import it
i
Yeah, if false is fine, then you don't need to add anything in popUpTo's trailing lambda.
👍 1
j
and… I’m not able to get it to work 😅 I go A -> B -> C -> D -> (B with popUpTo B) and pressing back takes me to C. Not sure if I’m thinking wrong about it but back should take me to B, right?
A, B and C are part of bottom nav. And C and D are in another graph. I think it should not affect, maybe I’m wrong
k
I thinks its IDE being finicky and not
importing
properly yet. I simply manually import.
i
Yeah, known issue with Studio and imports of the same name as a member function (despite them having different parameters), please star https://issuetracker.google.com/issues/172834438
👍 2
k
@Jordi Saumell Dont think you should keep A,B, C in backstack, given they are primary destinations at the bottom navigation. A good idea as suggested by the google docs would be anytime a primary destination is reached, to popbackstack upto the starting destination and then adding this new destination to backstack.
Copy code
onClick = {
                        navController.navigate(screen.route) {
                            // Pop up to the start destination of the graph to
                            // avoid building up a large stack of destinations
                            // on the back stack as users select items
                            popUpTo = navController.graph.startDestination
                            // Avoid multiple copies of the same destination when
                            // reselecting the same item
                            launchSingleTop = true
                        }
                    }
i
The most important part is that your route strings match exactly. If you had
composable("profile")
, it should be
popUpTo("profile")
, same with
composable("profile/{userId}")
and
popUpTo("profile/{userId}")
k
Yea otherwise that String.hashCode() will generate a different
Int
. Btw where is that implementation 👀 ?
j
yes, I’ve build constants to make sure I always use the same string, and in this case there aren’t event parameters, so I couldn’t get them wrong because of that
👍 1
@KD with your comment now I’m not sure if B should stay in the backstack or not, I thought so... But even if it didn’t stay in the backstack C should still be removed if I use popUpTo, shouldn’t it? So it should go to the start destination (which is A)
i
If
B
isn't on the back stack,
popUpTo(B)
does nothing
j
ok, then I suppose that will be the problem. And are top destinations (bottom bar) not supposed to be in the backstack? I thought so
k
This back stack :A -> B -> C -> D -> (B with popUpTo B) should lead you to A. Maybe check your code could be a bug.
i
So A should remain on the stack, but once you switch to C, B would be popped
(similarly, when you switched from C to B, C would be popped)
j
ok, thank you! 🙂 doing popUpTo startDestination does remove C and D. So the issue was because of B not being in the backstack as you said, and then popUpTo doing nothing
1
d
@Jordi Saumell How did you solve that int issue that needs to be passed into popUpto()
i
popUpTo also has an extension that takes a route - make sure to import that one
👌 1
267 Views