Search code examples
androidnavigationandroid-jetpack-compose

Nested navigation with bottom bar in Jetpack Compose


I have a RootGraph that contains the next code:

@Composable
fun RootNavGraph(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = CommonRoute.Splash.route,
        route = GeneralGraph.ROOT
    ) {
        composable(CommonRoute.Splash.route) { SplashScreen(navController = navController) }
        authNavigation(navController)
        composable(CommonRoute.MainBody.route) {
            MainBody()
        }
    }
}

I want to create a login flow, when your session expires one would want to navigate from the bottom graph back to the rootGraph.

@Composable
fun MainBody(navController: NavHostController = rememberNavController()) {
    Scaffold(
        bottomBar = {
            CustomBottomBar(navController = navController)
        }
    ) {
        Surface(
            modifier = Modifier
                .padding(it)
                .fillMaxSize()
        ) {
            MainNavGraph(navController)
        }
    }
}


@Composable
fun MainNavGraph(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = MainGraph.MAIN,
        route = GeneralGraph.MAIN
    ) {
        mainNavigation(navController = navController)
    }
}

fun NavGraphBuilder.mainNavigation(navController: NavHostController) {
    navigation(route = MainGraph.MAIN, startDestination = MainRoute.Home.route) {
        composable(MainRoute.Home.route) {
            HomeScreen(
                navController = navController,
                onUserNotRegistered = {
                    navController.navigate(GeneralGraph.AUTH) {
                        popUpTo(navController.graph.findStartDestination().id)
                    }
                }
            )
        }
        composable(MainRoute.Post.route) { PostScreen(navController = navController) }
        composable(MainRoute.Profile.route) {
            //val viewModel: ProfileViewModel = hiltViewModel()
            ProfileScreen(navController = navController, onSignOut = { })
        }
    }
}

At the start, it loads the Splash screen and goes to the Home Screen to check if a user exists, otherwise I want it to send me to the Auth graph. Likewise, when I log out from my profile screen that is in the options of my bottom bar, it sends me to the authentication graph.


Solution

  • first put below code in main activity

    val navController = rememberNavController()
    RootNavigationGraph(navController = navController)
    

    create new RootNavigationGraph.kt and put below code

    @Composable
        fun RootNavigationGraph(navController: NavHostController) {
            NavHost(
                navController = navController,
                startDestination = Screen.Login.route
            ) {
                composable(Screen.Login.route) {
                    PhoneLoginScreen(navController = navController)
                }
                composable(Screen.Home.route) {
                    BottomNavigationScreen(rootNavController = navController)
                }
            }
        }
    

    create new BottomNavigationScreen.kt and put below code

    @Composable
    fun BottomNavigationScreen(rootNavController: NavController) {
        val navController = rememberNavController()
        val scaffoldState = rememberScaffoldState(
            drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
        )
    
        val activity = LocalContext.current as Activity
        val navBackStackEntry by navController.currentBackStackEntryAsState()
        val currentRoute = navBackStackEntry?.destination
        BackHandler {
            if (currentRoute?.route == BottomNavigationItem.HOME.route){
                activity.finish()
            }
        }
    
        Scaffold(
            scaffoldState = scaffoldState,
            bottomBar = { CustomBottomNavigation(navController = navController) }
        ) {
            Box(modifier = androidx.compose.ui.Modifier.padding(it)) {
                BottomNavigationGraph(
                    navController = navController
                )
            }
        }
    }
    

    create new BottomNavigationGraph.kt and put below code

    @Composable
    fun BottomNavigationGraph(
        navController: NavHostController,
    ) {
        NavHost(
            navController = navController,
            startDestination = Constants.BottomNavigationItemRoute.HOME
        ) {
            composable(BottomNavigationItem.HOME.route) {
                HomeScreen()
            }
            composable(BottomNavigationItem.PEOPLE.route) {
                // other composable screen
            }
            composable(BottomNavigationItem.TASKS.route) {
                // other composable screen
            }
        }
    }
    

    create new CustomBottomNavigation.kt and put below code

    @Composable
    fun CustomBottomNavigation(navController: NavController) {
        val items = mutableListOf(
            BottomNavigationItem.HOME,
            BottomNavigationItem.PEOPLE,
            BottomNavigationItem.TASKS
        )
        val navBackStackEntry by navController.currentBackStackEntryAsState()
        val currentRoute = navBackStackEntry?.destination
        BottomNavigation {
            items.forEach { item ->
                BottomNavigationItem(
                    icon = {
                        Icon(
                            painter = painterResource(id = item.icon),
                            contentDescription = item.title
                        )
                    },
                    label = {
                        Text(
                            text = item.title,
                            fontWeight = FontWeight.Normal,
                            fontSize = 10.sp,
                            style = TextStyle(letterSpacing = 1.8.sp),
                            maxLines = 1
                        )
                    },
                    selectedContentColor = Secondary,
                    unselectedContentColor = White,
                    alwaysShowLabel = true,
                    selected = currentRoute?.route == item.route,
                    onClick = {
                        navController.navigate(item.route) {
                            popUpTo(navController.graph.findStartDestination().id) {
                                inclusive = false
                                saveState = false
                            }
                            launchSingleTop = true
                            restoreState = false
                        }
                    }
                )
            }
        }
    }