How to Implement the Nested Navigation in Jetpack Compose?

Jetpack Compose Nested Navigation

In this article, we’ll learn how to implement the nested navigation in Jetpack Compose with the help of an example.

Prerequisites:

What is Nested Navigation?

Nested Navigation helps us to organize and structure the app’s navigation hierarchy. Using it, we can add a navigation graph inside another navigation graph.

For example, imagine we have four screens in our app. We add them to the graph like this:

@Composable
fun SetupNavGraph(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = "screen1_route"
    ) {
        composable(
            route = "screen1_route"
        ) {
            Screen1()
        }

        composable(
            route = "screen2_route"
        ) {
            Screen2()
        }

        composable(
            route = "screen3_route"
        ) {
            Screen3()
        }

        composable(
            route = "screen4_route"
        ) {
            Screen4()
        }
    }
}

The code looks fine with four screens, but what if we have 50 screens? If we add 50 composable() methods, it will be difficult to maintain the code. Using nested navigation, we can add the related screens to separate navigation graphs and include them in the main graph. Let’s see how to do it.

For this article, we’ll make an app with the following navigation hierarchy.

Navigation Hierarchy

The app has four screens – Home, Settings, Payments, and Plans, and two nested navigation graphs, Welcome and User. The two graphs are added to the main (Root) graph. The final output looks like this:

Nested Navigation in Jetpack Compose

Let’s start coding.

First, create an empty Compose project and open MainActivity. Create a Composable called AppUI() and call it from the onCreate().

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.navigation.compose.rememberNavController
import com.example.nestednavigationexample.navgraphs.SetupNavGraph
import com.example.nestednavigationexample.ui.theme.NestedNavigationExampleTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            NestedNavigationExampleTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Center,
                        horizontalAlignment = Alignment.CenterHorizontally
                    ) {
                        AppUI()
                    }
                }
            }
        }
    }
}

@Composable
fun AppUI() {

}

Next, create the following packages and Kotlin files in the project.

Project Files

There are two packages – navgraphs and screens.

The navgraphs has the following Kotlin files:

  • NavGraph.kt
  • UserNavGraph.kt
  • WelcomeNavGraph.kt

In the screens, we added our four Screens:

  • HomeScreen.kt
  • PaymentsScreen.kt
  • PlansScreen.kt
  • SettingsScreen.kt

The Screen.kt is a sealed class.

Let’s add the code in each file.

Screen.kt:

const val WELCOME = "welcome"
const val USER = "user"
const val ROOT = "root"

sealed class Screen(val route: String) {
    object Home: Screen(route = "home_screen")
    object Settings: Screen(route = "settings_screen")
    object Payments: Screen(route = "payments_screen")
    object Plans: Screen(route = "plans_screen")
}

It contains the list of all the screens. We added 3 constant values. They represent the corresponding route values for their nav graphs.

UserNavGraph.kt:

fun NavGraphBuilder.userNavGraph(navController: NavHostController) {
    navigation(
        startDestination = Screen.Payments.route,
        route = USER
    ) {
        composable(
            route = Screen.Payments.route
        ) {
            PaymentsScreen(navController)
        }

        composable(
            route = Screen.Plans.route
        ) {
            PlansScreen(navController = navController)
        }
    }
}

The navigation() method is used to create the nested graphs. It takes two parameters:

  • startDestination – The first screen that should be displayed in the graph.
  • route – It is a unique value for identifying this graph.

WelcomeNavGraph.kt

fun NavGraphBuilder.welcomeNavGraph(navController: NavHostController) {
    navigation(
        startDestination = Screen.Home.route,
        route = WELCOME
    ) {
        composable(
            route = Screen.Home.route
        ) {
            HomeScreen(navController)
        }

        composable(
            route = Screen.Settings.route
        ) {
            SettingsScreen(navController)
        }
    }
}

NavGraph.kt:

@Composable
fun SetupNavGraph(navController: NavHostController) {
    NavHost(
        navController = navController,
        startDestination = WELCOME,
        route = ROOT
    ) {
        welcomeNavGraph(navController = navController)
        userNavGraph(navController = navController)
    }
}

This is the root graph. We added our nested graphs.

HomeScreen.kt:

@Composable
fun HomeScreen(navController: NavHostController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.spacedBy(
            space = 12.dp,
            alignment = Alignment.CenterVertically
        ),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Home Screen",
            fontSize = 20.sp
        )

        Button(
            onClick = {
                navController.navigate(route = Screen.Settings.route)
            }
        ) {
            Text(text = "Open Settings")
        }

        Button(
            onClick = {
                navController.navigate(route = USER)
            }
        ) {
            Text(text = "Open Payments")
        }
    }
}

To navigate to the Payments, we added USER as the route. This is because Payments is the first screen in the user graph. We should mention the route of its graph.

PaymentsScreen.kt:

@Composable
fun PaymentsScreen(navController: NavHostController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.spacedBy(
            space = 12.dp,
            alignment = Alignment.CenterVertically
        ),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Payments Screen",
            fontSize = 20.sp
        )

        Button(
            onClick = {
                navController.navigate(route = Screen.Plans.route)
            }
        ) {
            Text(text = "Open Plans")
        }

        Button(
            onClick = {
                navController.popBackStack()
            }
        ) {
            Text(text = "Go Back")
        }
    }
}

PlansScreen.kt:

@Composable
fun PlansScreen(navController: NavHostController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.spacedBy(
            space = 12.dp,
            alignment = Alignment.CenterVertically
        ),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Plans Screen",
            fontSize = 20.sp
        )

        Button(
            onClick = {
                navController.popBackStack()
            }
        ) {
            Text(text = "Go Back")
        }
    }
}

SettingsScreen.kt:

@Composable
fun SettingsScreen(navController: NavHostController) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.spacedBy(
            space = 12.dp,
            alignment = Alignment.CenterVertically
        ),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(
            text = "Settings Screen",
            fontSize = 20.sp
        )

        Button(
            onClick = {
                navController.popBackStack()
            }
        ) {
            Text(text = "Go Back")
        }
    }
}

Open MainActivity and call the SetupNavGraph() in the AppUI().

@Composable
fun AppUI() {
    val navController = rememberNavController()
    SetupNavGraph(navController = navController)
}

Now, run the project. You will see the following output.

Nested Navigation in Jetpack Compose

This is all about nested navigation in Jetpack Compose. I hope you have learned something new. If you have any doubts, leave a comment below.

Related Articles:


References:

Leave a Comment