Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetpack Compose + Navigation - Nested navigation with BottomNavBar

I'm trying to implement the following screen flow using Jetpack Compose + Jetpack Navigation:

Navigation concept

Actually, i am able to code two singles cases:

  • SplashScreen --> HomeScreen (with no BottomNavBar)
  • HomeScreen (with BottomNavBar) --> Tabs

I'm not able to code the whole problem. In fact, i have an issue with the management of the NavHost. In the first case (SplashScreen -> HomeScreen) i need to call the NavHost at a high scope:

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContent {
        MyAppTheme {
            //init the Navigation Controller for screen navigation
            val navController = rememberNavController()

            //setup the Navigation Graph
            SetupNavGraph(navController)

while in the second case i need to call it in the innerPadding scope of the Scaffold composable:

fun MainScreen(navController: NavHostController) {

    Scaffold(
        bottomBar = {
            BottomNavBar(navController)
        }
    ) { //innerPadding scope
        //setup the Navigation Graph
        SetupNavGraph(navController)
    }
}

Please assume that SetupNavGraph() function works as intended (call NavHost to generate the navigation tree)

  • I tried to use two NavHost without success.
  • If i setup the NavHost in setContent() i'm able to load the splashscreen and move to an empty BottomNavBar screen. If i click on the BottomNavElements i'm able to navigate to the child tabs (in the example above "Favorite","Music","Places", "News") but the BottomNavBar disappears
  • I cannot setup NavHost in the innerPadding scope because this is loaded only after switching to the main screen (in the example above "Favorite Tab" + BottomBarNav)

The only workaround i found is generating the BottomNavBar composable in each of the BottomNav child tabs, but this generates a visible transition effect that i would like to avoid and, generally, doesn't seem a good practice.

like image 373
Frax Avatar asked Jul 24 '26 01:07

Frax


1 Answers

Ok, i found a solution. This are the steps to achieve the desired result:

  1. Create two different NavGraph, one for Splash->MainScreen and the other for the BottomNavBar
const val ROOT_ROUTE = "root"

@Composable
fun SetupRootNavGraph(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = Screen.FirstScreen.route,
        route = ROOT_ROUTE
    ) {
        composable(Screen.FirstScreen.route) { FirstScreen(navController)}
        composable(Screen.SecondScreen.route) { MainScreen(navController)}
    }
}
const val BOTTOM_BAR_ROUTE = "bottomBar"

@Composable
fun SetupNavGraphBottomBar(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = BottomBarScreen.FirstElement.route,
        route = BOTTOM_BAR_ROUTE
    ) {
        composable(BottomBarScreen.FirstElement.route) { FirstElementScreen() }
        composable(BottomBarScreen.SecondElement.route) { SecondElementScreen() }
        composable(BottomBarScreen.ThirdElement.route) { ThirdElementScreen() }
    }
}
  1. Init the NavController and the RootNavGraph after setContent() in your MainActivity. This will be in charge of the SplashScreen -> MainScreen navigation tree.
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyAppTheme {

                //init the Navigation Controller for screen navigation
                val navController = rememberNavController()

                //setup the Root Navigation Graph
                SetupRootNavGraph(navController)
            }
        }
    }
}
  1. Re-init the NavController in the Screen where you have your BottomNavBar ("MainScreen" in the example) and the assign to it the BottomNavGraph in the innerPadding scope.
@Composable
fun MainScreen(navController: NavHostController) {

    //Re-initialize the NavController to set a new NavGraph
    val navControllerBottomBar = rememberNavController()

    Scaffold(
        bottomBar = {
            BottomNavBar(navControllerBottomBar)
        }
    ) {
        //setup the Navigation Graph
        SetupNavGraphBottomBar(navControllerBottomBar, user)
    }
}

And this will work like charm! Of course you will need to structure your BottomNavBar in order to manage the navigation as documented on Official docs

like image 189
Frax Avatar answered Jul 26 '26 18:07

Frax



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!