Search code examples
androidandroid-jetpack-composeandroid-jetpack-navigation

Navigating to a Bottom Tab Screen from a Single screen with a button Jetpack Compose


I have a auth page and after the auth page, I basically navigate to a Tabbed application.

The problem is that bottom bar disappears once I click on tab.

Below is what my code looks like

sealed class ScreenM(val route: String) {
    object Landing: Screen("landingm")
    object Tab: Screen("tabm")
}

sealed class Screen(val route: String) {
    object PasswordLogin: Screen("passwordlogin")
    object TabBar: Screen("tabbar")
}

sealed class TabbarItem(var route: String, var icon: Int, var title: String) {
    object Home : TabbarItem("tabhome", R.drawable.ic_home_tab_icon, "Home")
    object Profile : TabbarItem("tabprofile", R.drawable.ic_profile_tab_icon, "Profile")
}

my application entry point is

@Composable
fun App() {
    val navController = rememberNavController()
    NavHost(navController, startDestination = ScreenM.Landing.route) {
        addLandingTopLevel(navController = navController)
        addTabBarTopLevel(navController = navController)
    }
}

private fun NavGraphBuilder.addLandingTopLevel(
    navController: NavController,
) {
    navigation(
        route = ScreenM.Landing.route,
        startDestination = Screen.Home.route
    ) {
        addPasswordLogin(navController)
    }
}
private fun NavGraphBuilder.addPasswordLogin(navController: NavController) {
    composable(route = Screen.PasswordLogin.route) {
        PasswordLoginView(navController)
    }
}

private fun NavGraphBuilder.addTabBarTopLevel(
    navController: NavController,
) {
    navigation(
        route = ScreenM.Tab.route,
        startDestination = Screen.TabBar.route
    ) {
        addTabBar(navController)
        addHome(navController)
        addProfile(navController)
    }
}

private fun NavGraphBuilder.addTabBar(navController: NavController) {

    composable(route = Screen.TabBar.route) {
        TabBarView(navController)
    }
}

private fun NavGraphBuilder.addHome(navController: NavController) {
    composable(route = TabbarItem.Home.route) {
        HomeView()
    }
}
private fun NavGraphBuilder.addProfile(navController: NavController) {
    composable(route = TabbarItem.Profile.route) {
        ProfileView()
    }
}

I am triggering the Tab like this

// ...
NavigationButton(buttonText = "Login", onBackPressed = {
    navController.popBackStack()
}) {
    navController.navigate(ScreenM.Tab.route)
}
// ...

Then my Tabbar is like

@Composable
fun TabBarView(navController: NavController) {
    Scaffold(
        bottomBar = { BottomNavigationBar(navController) }
    ) {
    }
}

Then bottom navigation Bar is like this

@Composable
fun BottomNavigationBar(navController: NavController) {
    val items = listOf(
        TabbarItem.Home,
        TabbarItem.Profile
    )
    BottomNavigation(
        backgroundColor = colorResource(id = R.color.white),
        contentColor = Color.Black
    ) {
        items.forEach { item ->
            BottomNavigationItem(
                icon = { Icon(painterResource(id = item.icon), contentDescription = item.title) },
                label = { Text(text = item.title) },
                selectedContentColor = Color.Red,
                unselectedContentColor = Color.Blue.copy(0.4f),
                alwaysShowLabel = true,
                selected = false,
                onClick = {
                    navController.navigate(item.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
                        navController.graph.startDestinationRoute?.let { route ->
                            popUpTo(route) {
                                saveState = true
                            }
                        }
                        // Avoid multiple copies of the same destination when
                        // reselecting the same item
                        launchSingleTop = true
                        // Restore state when reselecting a previously selected item
                        restoreState = true
                    }
                }
            )
        }
    }
}

Solution

  • You have two options.

    1. Displaying BottomNavigationBar inside each tab

      1.1. Not sure what's addTabBar navigation route in your code, I don't think it's need, as looks like you only have two tabs: TabbarItem.Home and TabbarItem.Profile.

      1.2. You can add BottomNavigationBar inside each view, specifying the selected item. Inside HomeView it can look like this:

      BottomNavigationBar(navController, selectedTab = TabbarItem.Home)
      

      1.3. Depending on the selected tab, you need to select the needed item of BottomNavigationBar

      @Composable
      fun BottomNavigationBar(navController: NavController, selectedTab: TabbarItem) {
          val items = listOf(
              TabbarItem.Home,
              TabbarItem.Profile
          )
          BottomNavigation(
              // ...
          ) {
              items.forEach { item ->
                  BottomNavigationItem(
                      // ...
                      selected = selectedTab == item,
                      // ...
      
    2. Having a single navigation bar outside of NavHost, you can find an example in documentation