Navigation System in NowInAndroid App

Navigation is a fundamental aspect of any Android application, especially in modern apps built with Jetpack Compose. In this blog, we will dive deep into the implementation of a navigation architecture that is modular, scalable, and easy to extend. The code we'll explore covers features such as top-level destinations, modular navigation for individual features, and deep links for specific resources.

This step-by-step guide will help you understand the architecture and how you can build a similar system for your application.

A big thanks to NowInAndroid Repository for providing the code example used in this blog, which was taken from their open-source project.

Understanding the Architecture

The provided implementation is structured to:

  1. Define Top-Level Destinations: Each top-level destination represents a primary section of the app.

  2. Utilize Modular Navigation: Each feature module handles its own navigation, making the app easier to maintain and scale.

  3. Handle Deep Links: Deep links allow users to navigate to specific screens directly from notifications or external links.

Step 1: Defining Top-Level Destinations

The TopLevelDestination enum defines the primary sections of the app:

enum class TopLevelDestination(
    val selectedIcon: ImageVector,
    val unselectedIcon: ImageVector,
    @StringRes val iconTextId: Int,
    @StringRes val titleTextId: Int,
    val route: KClass<*>,
    val baseRoute: KClass<*> = route,
) {
    FOR_YOU(
        selectedIcon = NiaIcons.Upcoming,
        unselectedIcon = NiaIcons.UpcomingBorder,
        iconTextId = forYouR.string.feature_foryou_title,
        titleTextId = R.string.app_name,
        route = ForYouRoute::class,
        baseRoute = ForYouBaseRoute::class,
    ),
    BOOKMARKS(
        selectedIcon = NiaIcons.Bookmarks,
        unselectedIcon = NiaIcons.BookmarksBorder,
        iconTextId = bookmarksR.string.feature_bookmarks_title,
        titleTextId = bookmarksR.string.feature_bookmarks_title,
        route = BookmarksRoute::class,
    ),
    INTERESTS(
        selectedIcon = NiaIcons.Grid3x3,
        unselectedIcon = NiaIcons.Grid3x3,
        iconTextId = searchR.string.feature_search_interests,
        titleTextId = searchR.string.feature_search_interests,
        route = InterestsRoute::class,
    ),
}

Key Points:

  • Each destination has icons for selected and unselected states.

  • Text resources are associated for accessibility and titles.

  • Each destination specifies its route and optional base route.

This structure ensures that top-level navigation is clearly defined and easily extendable.

Step 2: Creating the Navigation Host

The NiaNavHost function is the heart of the navigation system. It uses Jetpack Compose's NavHost to define the navigation graph:

@Composable
fun NiaNavHost(
    appState: NiaAppState,
    onShowSnackbar: suspend (String, String?) -> Boolean,
    modifier: Modifier = Modifier,
) {
    val navController = appState.navController
    NavHost(
        navController = navController,
        startDestination = ForYouBaseRoute,
        modifier = modifier,
    ) {
        forYouSection(
            onTopicClick = navController::navigateToTopic,
        ) {
            topicScreen(
                showBackButton = true,
                onBackClick = navController::popBackStack,
                onTopicClick = navController::navigateToTopic,
            )
        }
        bookmarksScreen(
            onTopicClick = navController::navigateToInterests,
            onShowSnackbar = onShowSnackbar,
        )
        searchScreen(
            onBackClick = navController::popBackStack,
            onInterestsClick = { appState.navigateToTopLevelDestination(INTERESTS) },
            onTopicClick = navController::navigateToInterests,
        )
        interestsListDetailScreen()
    }
}

Key Points:

  • NavHost: Centralizes navigation for the app.

  • Each section—forYouSection, bookmarksScreen, searchScreen—handles its own navigation logic.

  • Encapsulation: Each feature is isolated, making it easier to manage individual navigation logic.

Step 3: Modular Navigation for Features

Each feature module defines its own navigation. For example, the Bookmarks feature:

@Serializable object BookmarksRoute

fun NavController.navigateToBookmarks(navOptions: NavOptions) =
    navigate(route = BookmarksRoute, navOptions)

fun NavGraphBuilder.bookmarksScreen(
    onTopicClick: (String) -> Unit,
    onShowSnackbar: suspend (String, String?) -> Boolean,
) {
    composable<BookmarksRoute> {
        BookmarksRoute(onTopicClick, onShowSnackbar)
    }
}

Key Points:

  • BookmarksRoute: Defines the route for the bookmarks screen.

  • NavGraphBuilder.bookmarksScreen: Encapsulates the composable and its associated logic.

  • The feature's navigation is self-contained, ensuring modularity.

Similarly, the ForYou feature:

fun NavGraphBuilder.forYouSection(
    onTopicClick: (String) -> Unit,
    topicDestination: NavGraphBuilder.() -> Unit,
) {
    navigation<ForYouBaseRoute>(startDestination = ForYouRoute) {
        composable<ForYouRoute>(
            deepLinks = listOf(
                navDeepLink {
                    uriPattern = DEEP_LINK_URI_PATTERN
                },
            ),
        ) {
            ForYouScreen(onTopicClick)
        }
        topicDestination()
    }
}

Deep Links:

  • navDeepLink: Enables navigation to a specific screen using a URI pattern.

Step 4: Extending the Navigation System

To add a new feature module:

  1. Create a Route Object: Define a Serializable route object for the new module.

  2. Extend the NavGraphBuilder: Add a function to encapsulate the navigation logic for the feature.

  3. Update the NavHost: Add the new feature to the NiaNavHost.

For example, adding a Profile feature:

@Serializable object ProfileRoute

fun NavController.navigateToProfile(navOptions: NavOptions) =
    navigate(route = ProfileRoute, navOptions)

fun NavGraphBuilder.profileScreen(onLogout: () -> Unit) {
    composable<ProfileRoute> {
        ProfileScreen(onLogout)
    }
}

Benefits of This Architecture

  1. Modularity:

    • Each feature manages its own navigation.

    • Easy to add or remove features without affecting the core navigation logic.

  2. Scalability:

    • The architecture can handle multiple features and complex navigation flows.

  3. Maintainability:

    • Encapsulated logic makes it easier to debug and test individual features.

  4. Deep Link Support:

    • Users can navigate directly to specific screens from notifications or external sources.

Conclusion

This navigation architecture leverages Jetpack Compose’s powerful NavHost and Kotlin's modularity to create a system that is robust, scalable, and maintainable. By encapsulating navigation logic within individual modules, the architecture promotes clean code and reduces the risk of breaking changes.

Akshay Nandwana
Founder AndroidEngineers

You can connect with me on:


Book 1:1 Session here
Click Here

Join our upcoming classes
https://www.androidengineers.in/courses

Love from our past students

Excellent list of questions really helped me to coverup all the topics before interview.

Saiteja Janjirala

10th Oct, 2024

I had an exceptional experience with the 1:1 mentorship session. Akshay was incredibly friendly and provided invaluable guidance on focusing on long-term goals. They also gave great interview tips, including a thorough resume review. Additionally, the discussion on Data Structures and Algorithms (DSA) was insightful and practical. Highly recommended for anyone looking to advance their career!

Nayab khan

11th Sep, 2024

Cleared my major points for what I am missing in the resume and also suggested what I can work on for further growth in the career.

Ketan Chaurasiya

7th Aug, 2024

What impressed me most was his personalized approach and practical tips that I could immediately apply. Akshay’s guidance not only improved my technical skills but also boosted my confidence in navigating my career path. His insights and encouragement have been a game-changer for me. I highly recommend Akshay’s mentorship to anyone looking to advance their Android development career.

Hardik Kubavat

5th Aug, 2024

2025© Made with   by Android Engineers.