Mastering Nested Navigation in Jetpack Compose with Bottom Bar Integration | by Rakib Hasan | Nov, 2024

This is the visual interpretation of my project’s nested navigation graph.

Kicking things off with the essentials: Let’s dive into adding the Navigation Dependency.

https://gist.github.com/rakibhasan1030/f847e5bc3776451320b1af92e4bff1f5

This Graph.kt is created to keep the navigation system organized and easy to manage.

Graph.kt https://gist.github.com/rakibhasan1030/445ff6dd34e4f70c320c6641c5584a9a

The ROOT graph is the top-level container for the entire app’s navigation. The AUTHENTICATION graph handles all the screens related to login and registration, while the HOME graph manages the main app screens. I also added A_NESTED as a child of the HOME graph to handle deeper navigation within specific sections. This makes the navigation clean, simple, and easy to expand as the app grows.

MainActivity.kt https://gist.github.com/rakibhasan1030/fb21c880679881f0585ac7493ad6a2cb

The navController is used to manage navigation within the app. It keeps track of the current screen and allows navigating between different destinations. I use rememberNavController() to create and remember the navigation controller, ensuring that it persists across recompositions. This makes it possible to control the flow of the app’s screens efficiently.

Let’s dive into the navigation setup!

In this phase, we map out the destinations based on their hierarchy, beginning with the root graph and progressively adding other navigation graphs as needed. As part of this process, I’ve also incorporated token-based authentication to ensure a secure and personalized experience for users. Let’s take a closer look at the RootNavGraph and how this approach aligns with a tokenized system.

RootNavGraph.kt https://gist.github.com/rakibhasan1030/193172dc031ee34e9ddf7033112cdc70

In this code, the RootNavGraph defines the core navigation structure for the app. The starting point is dynamically determined based on the presence of a token. If the token is absent, the app will navigate to the AUTHENTICATION graph, where users can log in or register. However, if the token is available, the app directly navigates to the HOME graph, granting access to the main app screens.

In the authNavGraph, we’re setting up the navigation flow for the authentication screens, including Login and Register. These screens are part of the AUTHENTICATION graph, and the navigation actions between them and the main HOME graph are controlled by the NavController.

AuthNavGraph.kt https://gist.github.com/rakibhasan1030/e6bda4abb9aaada8d9736b4886a8b2c2

This is a key point to understand:

When the user successfully logs in, the code in this block is triggered:

onSuccessfulLoggedIn = {
navController.navigate(HOME) {
popUpTo(AUTHENTICATION) { inclusive = true }
launchSingleTop = true
}
}

Here’s how it works:

  • navController.navigate(HOME): This tells the navigation controller to move to the HOME screen after a successful login.
  • popUpTo(AUTHENTICATION) { inclusive = true }: This is a crucial step in managing the backstack. By calling popUpTo with inclusive = true, we remove the entire AUTHENTICATION graph (which includes the Login and Register screens) from the backstack. This ensures that once the user logs in, they cannot navigate back to the login or registration screen using the back button. If they press the back button, they’ll be taken out of the app or to the home screen, but they won’t return to the authentication flow unless they log out and manually navigate back to it.
  • launchSingleTop = true: This ensures that the HOME screen is not duplicated if it’s already on top of the backstack. It prevents multiple instances of the same screen, improving efficiency and keeping the navigation state clean.

This backstack management is a best practice, ensuring that once the user logs in, they can’t accidentally return to the login screen, thus maintaining a smooth user experience. The only way to return to the login or registration screens is through manual navigation (e.g., logging out).

Here’s a simplified visual representation of the flow, showing how the backstack evolves:

Scenario 1 (Login Flow):
Start -> AUTHENTICATION -> Login -> HOME
Backstack After Login: HOME
// (All previous screens are cleared, leaving only HOME in the backstack.)

Scenario 2 (Register Flow):
Start -> AUTHENTICATION -> Login -> Register -> HOME
Backstack After Registration: HOME
// (All previous screens, including AUTHENTICATION, Login, and Register, are cleared, leaving only HOME in the backstack.)

Scenario 3 (Token Available):
Start -> HOME
Backstack: HOME
// (Since the token is available, the user is directly taken to the HOME screen, skipping the AUTHENTICATION flow entirely.)

Also, I use sealed classes for routing to make navigation more structured and type-safe. By defining each route as a specific object within the sealed class, I ensure there’s no chance of typos or invalid routes. It keeps the navigation flow clear, maintainable, and easy to extend as the app grows.

BottomBarScreen.kt https://gist.github.com/rakibhasan1030/d289b20d1a21a1037b65521f88c9e141

BottomBarScreen.kt https://gist.github.com/rakibhasan1030/d289b20d1a21a1037b65521f88c9e141

The HomeNavGraph sets up the main navigation flow for the home section of the app, where users can navigate between different screens related to the home functionality.

Now, here is the BottomBarScreen, which defines the HomeNavGraph. Notice that I’ve passed two NavControllers intentionally. The bottomNavController is used to navigate within the home section, specifically between different screens in the HomeNavGraph. But what happens if the user logs out from the home graph? Since the bottomNavController doesn’t have access to the AUTHENTICATION graph, it can’t navigate to the login or registration screens.

HomeNavGraph.kt https://gist.github.com/rakibhasan1030/be787fbba46a497a5c442bc8a898b15d

HomeNavGraph.kt https://gist.github.com/rakibhasan1030/be787fbba46a497a5c442bc8a898b15d

The HomeNavGraph sets up navigation for the home section, allowing users to switch between ScreenA and ScreenB using the bottom bar. From ScreenA, users can navigate to a nested graph (A_NESTED).

ScreenANestedGraph.kt https://gist.github.com/rakibhasan1030/517e408927fb9d2e2939deba386115fb

In the screenANestedNavGraph, I’ve set up a nested navigation flow for ScreenA. The graph starts with ScreenAChild1, where users can navigate forward to ScreenAChild2, and from there to ScreenAChild3. Each screen has back navigation handled by navController.navigateUp(), which takes the user back to the previous screen in the stack. The final node of this graph is ScreenAChild3, marking the deep nested routing of the flow. If the user presses “Pop Out To First,” they are redirected to the HOME screen, clearing the stack and ensuring a clean transition back to the home graph. This setup ensures a well-structured navigation flow, especially for deep nested routes within the home section.

Folder Structure Overview

Explore the full code in my GitHub repository. I hope this project proves to be a valuable resource for you.

I’m open to any feedback or suggestions. If you need assistance or code reviews, don’t hesitate to reach out.

Happy composing with Jetpack Compose! 🎉

Leave a Reply