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

How do you implement Android Compose nested navigation properly with BackdropScaffold


I am investigating the use of Jetpack Compose navigation in my current Android application.

my apps "Home" screen uses androidx.compose.material.BackdropScaffold and its frontLayerContent is set as follows:-

frontLayerContent = { MyNavigationHost(navController = navigator) }

this all works as required for the top level destinations in the app, as these top level navigation destinations are expected to be hosted in the backdrop.

however from each of these top level screens (which host a List of items) the user should be able to click on a list item and navigate to a lower level screen where the backdrop is no longer visible, and the user will see a simple tool bar with the Up arrow to navigate up a level back to the parent list screen.

i have created nested navigation targets which i can reach however i have two issues the backdrop is always visible even in the lower level detail screens

and when the user goes back it, they navigate to the Home screen (Start destination) and not the actual high level screen they were on viewing the list.

do i need multiple navHosts? one that has all the top level screens that are contained in the backdrop frontContent?

or can i some how use one navHost and specify the frontContentLayer should only display the highest level screens?

UPDATE

What I am attempting to achieve is this:-

Top level screens T1, T2, T3, & T4 are all listed on the backdrop of my application main screen where the backdrop frontcontent is set as shown above; e.g.

frontLayerContent = { MyTopLevelNavigationHost(navController = navigator) }

when the user has navigated to any of these top level screens they should still be able to see the "Hamburger" toolbar and access the backdrop. All of this is working as required currently in my application.

however when the user navigates to a level below any of these top level screens i need to replace the "Hamburger" toolbar with a simple toolbar showing the Up arrow and a basic toolbar title.

i realise i need a second NavHost for these "lower level" screens, that allows the user to navigate the separate sub-navgraph and lets me replace the replace the "Hamburger" toolbar with a simple toolbar showing the Up arrow and a basic toolbar title. I do not understand how to implement this second "lower level" navHost.

i cannot see how to "override" the frontcontent of my backdropscaffold to show the lower level screens.


Solution

  • do i need multiple navHosts? -> yes

    I believe the proper way is having a navgraph for backdrop, passing scaffold state to each destination shown in front layer in order to close it when necessary and having a top level controller for backdrop scaffold in case you need to change the content shown in backdrop layer.

    //EDIT STARTS

    I really wanted to give you an example in code but this will take too much time from my side. However, I will divide what you can do into understandable steps in order to achieve what you want.

    • First, create a top-level nav host that contains your backdrop scaffold screen and other top-level screens. With this, you basically put each top-level screen in the same level hierarchy in your navgraph. Hence, as long as you have access the controller, you can navigate to each screen.

    You can easily access the controller without passing it to each screen by doing something like that:

    // I put these in my Theme.kt as the composable itself 
    // has the topmost hierarchy in each composable node
    
    val LocalNavigationManager = compositionLocalOf<NavHostController> { error("No nav host found") }
    
    // Then in your MaterialTheme composable
    @Composable
    fun MyTheme(isDark: Boolean = isSystemInDarkTheme()){
         val navController = rememberNavController()
        MaterialTheme(
            colors = colors,
            typography = typography,
        ) {
            CompositionLocalProvider(
                 LocalNavigationManager provides navController
            )
        }
    
    }
    

    And while building NavHost, pass controller as LocalNavigationManager.current

    • Second, create a sub-navgraph for your front layer in your backdrop scaffold. However, don't put that navgraph in your top-level hierarchy. Use this only in your front-layer. You can put any of your top-level screens in this navgraph or you can navigate to any top-level screen by using the top-level controller you can acquire from "LocalNaigationManager.current". With this you can both navigate in front-layer hierarchy or top-level hierarchy independently.

    I hope this helps, I will edit again if you have any other questions.

    Very Late Edit for Hector's Comment What I mean by sub-navgraph is no different than a typical navgraph. Just create another NavHost for your front layer, and rather than calling a composable directly in the front layer, call the nav-graph you created. As both the front-layer and back-layer have different navhosts, they will still navigate independently.