5

I want to have a bottom navigation bar with two items/screens: Order and Account. Order is the start destination. Order has its own navigation and it has two screens: ItemList and ItemDetail. ItemDetail opens when an item is clicked in ItemList screen.

When I run the app, I can see the ItemList screen but Order item in the bottom navigation bar is not selected. If I click on Account item, I can see Account screen and Account item gets selected in the bottom navigation bar.

I think this is happening because of the recomposition: when Order is selected at the beginning since it is the start destination, its nested graph is called and a new destination (ItemList) is navigated, leading a recomposition, with currentRoute being "itemList" rather than "order".

How can I get Order icon selected in the bottom navigation bar? Is there a recommended what of handling nested graphs with bottom nav?

This is what I have at the moment:

object Destinations { const val ORDER_ROUTE = "order" const val ACCOUNT_ROUTE = "account" const val ITEM_LIST_ROUTE = "itemList" const val ITEM_DETAIL_ROUTE = "itemDetail" const val ITEM_DETAIL_ID_KEY = "itemId" } class NavigationActions(navController: NavHostController) { val selectItem: (Long) -> Unit = { itemId: Long -> navController.navigate("${Destinations.ITEM_DETAIL_ROUTE}/$itemId") } val upPress: () -> Unit = { navController.navigateUp() } } class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MyApp() } } } @Compose fun MyApp() { MyAppTheme { val navController = rememberNavController() val tabs = listOf(Destinations.ORDER_ROUTE, Destinations.ACCOUNT_ROUTE) val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.arguments?.getString(KEY_ROUTE) Scaffold( bottomBar = { BottomNavigation { tabs.forEach { tab -> BottomNavigationItem( icon = { Icons.Filled.Favorite }, label = { Text(tab) }, selected = currentRoute == tab, onClick = { navController.navigate(tab) { popUpTo = navController.graph.startDestination launchSingleTop = true } }, alwaysShowLabel = true, selectedContentColor = MaterialTheme.colors.secondary, unselectedContentColor = LocalContentColor.current ) } } } ) { NavGraph(navController) } } } @Composable fun NavGraph( navController: NavHostController, startDestination: String = Destinations.ORDER_ROUTE ) { val actions = remember(navController) { NavigationActions(navController) } NavHost(navController = navController, startDestination = startDestination) { navigation(startDestination = Destinations.ITEM_LIST_ROUTE, route = Destinations.ORDER_ROUTE) { composable(Destinations.ITEM_LIST_ROUTE) { ItemList(actions.selectItem) } composable( "${Destinations.ITEM_DETAIL_ROUTE}/{$Destinations.ITEM_DETAIL_ID_KEY}", arguments = listOf(navArgument(Destinations.ITEM_DETAIL_ID_KEY) { type = NavType.LongType }) ) { ItemDetail() } } composable(Destinations.ACCOUNT_ROUTE) { Account() } } } 

2 Answers 2

2

I wrote this article with a similar example. It's in Portuguese but if you translate the page to English you'll get the idea... Also, you can find the sources here.

I think the problem is happening because you're using just one NavHost for the entire app. In fact, I guess you need to use one NavHost for each tab, then when the user select a tab, you must change the current NavHost.

oh! my article is based on this post here, which can also help you.

Sign up to request clarification or add additional context in comments.

Comments

2

The official documentation indeed has a flaw, it simply doesn't work. I have a workaround:

Create a state of the selected tab:

val items = listOf( TabScreens.ConversationOverview, // All screens ) val startDestination = TabScreens.ConversationOverview var selectedTab by remember { mutableStateOf(items.indexOf(startDestination)) } 

When creating your BottomNavigationItem change your items.forEach to a forEachIndexed:

items.forEachIndexed { index, screen -> val isSelected = index == selectedTab 

Pass in the isSelected variable to the selected parameter and set the index in the onClick lambda:

BottomNavigationItem( selected = isSelected, onClick = { selectedTab = index 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.