I have implemented a bottom nav bar same as in the...
# compose
m
I have implemented a bottom nav bar same as in the docs: https://developer.android.com/jetpack/compose/navigation#bottom-nav It's at the root of the app, and I would expect a back press to close the app, and it does when at the start destination. But at another screen, it navigates to the start screen instead of going out of the app. I would expect going to another screen to clear the backstack so the new screen is at the root of the backstack, but that doesn't happen. Is there something I am missing? I would expect
popUpTo
with
include = true
to result in the behavior I want, but it does not.
Solved: I needed to use
popUpTo(0)
, which doesn't seem documented anywhere.
s
The doc for popUpTo seem to say
Copy code
Pop up to a given destination before navigating. This pops all non-matching destinations from the back stack until this destination is found.
So I guess it will try to pop everything since none of your destinations will have an ID of 0? But with that said, I don’t think what you explain is how navigation works with this library, it always expects you to have a fixed startDestination, so going back should always lead to there before it leads to exiting the app. When you say “would expect going to another screen to clear the backstack so the new screen is at the root of the backstack” do you mean that you’d expect this to happen by default? Might be interesting to give this https://developer.android.com/guide/navigation/navigation-principles#fixed_start_destination a read to see what I mean by that
m
When you say “would expect going to another screen to clear the backstack so the new screen is at the root of the backstack” do you mean that you’d expect this to happen by default?
Not by default, but when I use popUpTo(startDestination) with
inclusive = true
.
it always expects you to have a fixed startDestination, so going back should always lead to there before it leads to exiting the app.
That's a fair default, but if the app contains 3 screens switchable with a bottom navbar, then none of the screens is more "root" than the other. Screen A is the start destination, but pressing back at screen B shouldn't switch you to screen A. Switching screens with a bottom navbar should not populate the backstack.
i
Nope. The screen you start the user on should be the screen they exit the app from. This has been confirmed through multiple UX studies we've ran (which is where those Principles came from) - accidentally leaving the app is a huge net negative for users
s
What do these studies say regarding apps that have distinct logged in and logged out situations that do not have a start destination which works for both logged in and logged out users? If you follow the steps from

this video

you end up with being redirected to a login screen on top of the backstack. Pressing back will actually make you exit the app if your start destination does not support being in a logged out state, as suggested at 9:30 in that video. And further explained in 9:42 where popping to the start destination with
Copy code
val startDestination = navController.graph.startDestination
navController.navigate(startDestination, navOptions { popUpTo(startDestination) {inclusive = true } })
is only suggested if “this would be appropriate if your start destination does not require login”. Meaning that if it does not you need to exit the app. And since you’re doing all this from the top of the backstack you don’t get the predictive back gesture animation either which would help the users understand that they are exiting the app. Not to mention in Android 14 they’d actually get the predictive back gesture which shows they’ll stay in the app, then that screen comes up,
navController.currentBackStackEntry!!.savedStateHandle
and from it
savedStateHandle.getLiveData<Boolean>(LOGIN_SUCCESSFUL)
is fetched and it is
false
, and then you exit the app, completely changing the expectations the users have when seeing that animation as they don’t stay in the app.
m
Nope. The screen you start the user on should be the screen they exit the app from. This has been confirmed through multiple UX studies we've ran
All right then, that is useful information! I feel like this could be mentioned here that it's intentional, it just felt like a bug to me.
i
That the code we provide follows the Principles of Navigation?
m
I get that there should be a fixed starting destination, it's just that I thought of the container with the navbar as that fixed destination. I went to the screen with the navbar, I press back, I go back to where I was before I saw the navbar, regardless if I used the navbar to switch the "internal" screen or not. The page itself says we want to avoid building a backstack as the user selects items. That "special case" of actually keeping the initial navbar screen on the backstack may seem obvious to you but it wasn't obvious to me, even after rereading about the Principles of Navigation now. This is different to having tabs on top of the screen, where pressing back does go back to before the screen-with-tabs, without going to the initial tab first, even though it seems like the same UI, just on top of the screen or the bottom.
i
Ah, tabs vs bottom nav have always been very distinct concepts for very, very distinct use cases in terms of the Material guidance; they've never actually been interchangeable, but I could see how that could be thought to be the case
m
I can see that now, although the Material Design 3 guidance for navbar says to use tabs if you have less than 3 or more than 5 entries, which makes it seem like they are interchangeable components, not for a separate purpose. The Google Chat app for example has a navbar with 2 entries (Chat, Spaces), which makes sense to me now knowing they have different behavior from tabs, but if I just looked at the M3 guidance I would think it should be using tabs (because it makes it seem like it's just a matter of the number of entries, not actually having different behavior for a different use case).