Does someone know how to setup a Instagram-Like Bo...
# compose
a
Does someone know how to setup a Instagram-Like BottomNavBar? Example: The “Home” Icon in the BottomNavBar stays selected even when the user clicks on a profile or the comments of a post. Also when the user clicks the “Home” icon again it goes back to the feed but only if he already selected the Home tab. When the user is on the Explore tab and clicks back to Home he continues right where he left off. I tried it with Nested Navigation but couldn’t get it right, am I on the right path?
i
Assuming each tab has its own nested navigation graph that contains its destinations, everything but the reselection behavior you want should work just by following the documentation: https://developer.android.com/jetpack/compose/navigation#bottom-nav
And if you want the reselection behavior, your onClick can use the same logic as
selected
to know whether to trigger a
navController.popBackStack("home", inclusive = false)
or a
navigate
call
a
@Ian Lake How can I check on my BottomNavBar if I’m still on the same Graph? Here:
selected = currentRoute == item.route
I want to to check if those two routes are on the same NavGraph “HomeGraph”. So for example “FeedScreen” and “PostCommentScreen” are both inside “HomeGraph”
s
You can check the backstack and see if any of the destinations in that list is the home one.
i
The
hierarchy
call in the code I linked is looking at the nested graph hierarchy - that's how you tell what graph the current destination is in
That's why I said if you are using a nested graph for each tab (which means you are using the hierarchy to determine your selected item), then the same code works for checking what is already selected
The important part is that the selected logic and the onClick logic needs to be same to actually tell when an onClick is a reselection or a new selection
a
Thank you guys so much for your help!
@Ian Lake One last thing, if I now want to navigate to a other users profile I can do that from every screen: Home, Explore etc. Normaly I would only have one composable:
Copy code
composable(
    "communityProfile/{userId}",
    arguments = listOf(navArgument("userId") { type = NavType.StringType })
) {
...
}
Now that I want to keep the bottomNavBar Icon selected I would have to put this code in every NavGraph? For example if the user is on “feed” which is a sub route of “HomeGraph” and now he navigates to “communityProfile/$id” it should still have the Home Icon selected. But he can also navigate to “communityProfile/$id” from the ExploreGraph, in this case it should keep the Explore Icon in the BottomNavBar selected. I’m bad at explaining but I hope you go what I’m trying to say
i
It sounds like you don't want to use the hierarchy of your graph to determine the selected bottom nav item at all, but instead might just want to have your own source of truth, separate from the NavController, which holds what the last selected item is? Then you can navigate to any destination without changing the selected bottom nav item. That's a totally valid thing to do
a
@Ian Lake Wow, you helped me a lot! Thank you so so much. Now its exactly how I wanted it to be
s
I’ve also seen the approach where a route can be build by also passing the root along, so that you may be at let’s say a
ShowDetail
screen both coming from a bottom nav screen named
Home
and from one named
Shows
where the route would be
home/show/{showId}
and
shows/show/{showId}
respectively. That would mean that you would still be able to derive which bottom nav item is highlighted from the
hierarchy
. See an example of this being done in the tivi repo. Ian do you have any opinions on this approach? I don’t think I’ve seen you discuss it in here before, but may have just missed it.
i
Well no, that technique isn't looking at the actual
hierarchy
at all - nothing in the structure of the actual graph (the
NavDestination
class and its
parent
NavGraph
) is being used there. It is essentially creating a second source of truth when one is already available to you as part of how you structure your graph (which also means you could change one and forget to change the other). Note that when you get to deep linking to a destination, the only thing that actually matters in building the correct synthetic back stack is the actual structure of your graph, which mean you really, really need to structure your graph correctly to make sure that back stack is correct
Of course, just like a website or any RESTful kind of setup, being consistent with how you structure your routes is going to be a good idea, so the general pattern of how to build your routes is good, but trying to use that as some sort of encoded information is probably not the best idea. If you really wanted to encode additional information alongside your destination, you'd instead want to use an argument based approach with a
defaultValue
as per the previous discussion: https://kotlinlang.slack.com/archives/CJLTWPH7S/p1628542716119200?thread_ts=1628541488.118700&cid=CJLTWPH7S
For example, in Aaron's case, his
communityProfile
destination should actually be located in the nested graph associated with the tab that he wants selected when you deep link to that destination - e.g., if you want it to be Home -> community profile, it should be in the home graph
s
Hmm but if communityProfile is reachable from both nested graphs, like from inside the home and another nested graph, it’d still work to just look at the hierarchy in order to determine which bottom nav item to show as selected no? The argument-based approach sounds interesting btw, gonna be interesting if the default of
true
or
false
would make more sense. Both sound reasonable to me, depending on what the app usually likes to show.
i
As per the deep link documentation, the important bit is that Navigation is automatically building the back stack for you. That back stack is 100% based on what your nested navigation graph structure looks like so if you want the system back button to do the right thing when you deep link to a destination (say, from a notification or from a link in another app), then that should be what is driving your navigation structure
If you have that other source of truth for your selected bottom nav, that does make selecting the right tab when deep linking to a destination a bit trickier yes (since you are no longer able to rely on the NavController as your source of truth - the NavController might be in the right state, but your
rememberSaveable
might not be...unless all of your deep links are to your first tab of course 😄)
a
@Ian Lake How can I navigate the BottomNavigationBar programatically from a function? I took the code from @star_zero:
Copy code
BottomNavigation {
    var selectedIndex by rememberSaveable { mutableStateOf(0) }

    tabs.forEachIndexed { index, tab ->
        BottomNavigationItem(
            selected = selectedIndex == index,
            onClick = {
                if (selectedIndex == index) {
                    (navController.findDestination(tab.route) as? NavGraph)?.let {
                        navController.popBackStack(it.startDestinationId, false)
                    }
                } else {
                    navController.navigate(tab.route) {
                        popUpTo(navController.graph.id) {
                            saveState = true
                        }
                        launchSingleTop = true
                        restoreState = true
                    }
                    selectedIndex = index
                }
            },
            icon = {
                Icon(imageVector = tab.icon, contentDescription = null)
            },
            label = {
                Text(text = tab.label)
            },
        )
    }
}
How can I programatically navigate from HomeGraph to ProfileGraph without pressing the Profile Item on my BottomNavBar?
Copy code
navController.navigate(BottomNavigationItem.ProfileGraph.route) { 
    navController.graph.startDestinationRoute?.let { route ->
        popUpTo(route) {
            saveState = true
        }
    }
    launchSingleTop = true
    restoreState = true
}
This does navigate to the ProfileGraph but within the HomeGraph, so it keeps the Home Icon selected. This is not the behaviour I would’ve expected 🤔
i
You have a separate source of truth for your selected bottom nav item, so you'll need to update that as well
Also note you never need to mention anyone already in the thread, we're already here.