Type Safe Nested Navigation in Jetpack Compose

Poulastaadas
5 min readJul 5, 2024

--

In This artical we will learn about new type safe nested navigation in jetpack compose.

I hope you already know about navigation in jetpack compose.If you are new please check out this links:

  1. Navigation with jetpack compose.
  2. Jetpack navigation codelab.

Why Nested Navigation ?

In Nested Navigation we can create smaller group semilier kind of screens together rather than putting all of the screens in liner order.

Let’s Take an example:

let’s say we have a few screens SPLASH , EMAIL_LOGIN , EMAIL_SIGNUP, HOME , LIBRARY. Now if we try to navigate wihtout nested navigation the graph looks something like this:

SPLASH →EMAIL_LOGIN — EMAIL_SIGNUP → HOME — LIBRARY

Now if we devide EMAIL_LOGIN , EMAIL_SIGNUP under auth and HOME , LIBRARY under App, then it’s simpler to understant and scalable.

SPLASH

AUTH

  • EMAIL_LOGIN
  • EMAIL_SIGNUP

APP

  • HOME
  • LIBRARY

This is The Implementation:

Let’s Define the Graph:

@Serializable
sealed class Screens {
@Serializable
object Auth {
@Serializable
object EmailSignUp

@Serializable
object EmailLogIn
}

@Serializable
object App {
@Serializable
data class Home(
val name: String = "default",
)
}
}

In this code we defined the Screens and we are grouped them under some geenreal name. The Kotlin @Serializable annotation is importent for it to work.

Let’s Define The NavHost:

@Composable
fun NestedNavStartDestination() {
val navController = rememberNavController()

NavHost(
navController = navController,
startDestination = Screens.Auth
) {
authGraph(navController)
appGraph(navController)
}
}

startDestination = Screens.Auth
we are defining Auth group as Start Destination.

Now let’s craete 2 NavGraphBuilder extention function named:
1. authGraph

2. appGraph

private fun NavGraphBuilder.authGraph(
navController: NavHostController,
) {
// navigation extention fun
}
private fun NavGraphBuilder.appGraph(
navController: NavHostController,
) {
// navigation extention fun
}

Creating extention function increasses code readabality. and keeps it clean.Now let’s put the intented screens on this extentin functions.

private fun NavGraphBuilder.authGraph(
navController: NavHostController,
) {
navigation<Screens.Auth>(
startDestination = Screens.Auth.EmailLogIn
) {
composable<Screens.Auth.EmailLogIn> {
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Spacer(modifier = Modifier.weight(1f))

Text(
text = "Email LogIn Screen",
fontSize = MaterialTheme.typography.titleLarge.fontSize,
fontWeight = FontWeight.SemiBold
)

Spacer(modifier = Modifier.weight(3f))

Text(
text = "Email SingUp",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(Screens.Auth.EmailSignUp)
}
)

Spacer(modifier = Modifier.weight(1f))

Text(
text = "Auth Success",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(
Screens.App.Home(
name = "Old User"
)
)
}
)

Spacer(modifier = Modifier.weight(3f))
}
}

composable<Screens.Auth.EmailSignUp> {
Column(
modifier = Modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Spacer(modifier = Modifier.weight(1f))

Text(
text = "Email SignUp Screen",
fontSize = MaterialTheme.typography.titleLarge.fontSize,
fontWeight = FontWeight.SemiBold
)

Spacer(modifier = Modifier.weight(3f))

Text(
text = "Email LogIn",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(Screens.Auth.EmailLogIn)
}
)

Spacer(modifier = Modifier.weight(1f))

Text(
text = "Auth Success",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(
Screens.App.Home(
name = "New User"
)
)
}
)

Spacer(modifier = Modifier.weight(3f))
}
}
}
}

A lot of thing went on this section let’s break it down.

composable<Screens.Auth.EmailLogIn> {

}

composable<Screens.Auth.EmailSignUp> {

}

This are the main composable block in which we created designated composable screens.

In composable<Screen.Auth.EmailLogIn> we created a simple composable where :

Text(
text = "Email SingUp",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(Screens.Auth.EmailSignUp)
}
)

This block of code navigate to EmailSignUp Screen.

Text(
text = "Auth Success",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(
Screens.App.Home(
name = "Old User"
)
)
}
)

This block of code navigate us to Home Screen.

Now Our Home Screen takes expects a paramter to navigate and this is the beauty of type safe naviigation we can just create a object of the class that Home Scrren expects and pass it on navigation().

For composable<Screen.Auth.EmailSingup>:

Text(
text = "Email LogIn",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(Screens.Auth.EmailLogIn)
}
)

This block of code navigate us to EmailLogIn Screen.

 Text(
text = "Auth Success",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(
Screens.App.Home(
name = "New User"
)
)
}
)

This will navigate us to Home Scrren with param “New User”.

This is the composable for Home Screen.

private fun NavGraphBuilder.appGraph(
navController: NavHostController,
) {
navigation<Screens.App>(
startDestination = Screens.App.Home::class
) {
composable<Screens.App.Home> {
Column(
modifier = Modifier
.fillMaxSize()
.navigationBarsPadding()
.padding(56.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
val user = it.toRoute<Screens.App.Home>().name

Spacer(modifier = Modifier.weight(2f))

Text(
text = "Home Screen",
fontSize = MaterialTheme.typography.titleLarge.fontSize,
fontWeight = FontWeight.SemiBold
)

Spacer(modifier = Modifier.weight(1f))

Text(
text = "Hello $user",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(Screens.Auth.EmailLogIn)
}
)

Spacer(modifier = Modifier.weight(3f))
}
}
}
}

Notice as Home Screen takes a parameter we are definig KClass on start destitaion.

startDestination = Screens.App.Home::class

And on this block of code we just navigate back to Email Login Screen

Text(
text = "Hello $user",
modifier = Modifier.clickable {
navController.popBackStack()
navController.navigate(Screens.Auth.EmailLogIn)
}
)

Please Keep in mind Only use Nested Navigation if you have a very complex project. Don’t make simple porjects un-necessarily complex. Jetpack Navigation examples are moe than enough for porject with fewer screens.

--

--