.png&w=3840&q=75)
.png)
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:
Define Top-Level Destinations: Each top-level destination represents a primary section of the app.
Utilize Modular Navigation: Each feature module handles its own navigation, making the app easier to maintain and scale.
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:
Create a Route Object: Define a
Serializable
route object for the new module.Extend the NavGraphBuilder: Add a function to encapsulate the navigation logic for the feature.
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
Modularity:
Each feature manages its own navigation.
Easy to add or remove features without affecting the core navigation logic.
Scalability:
The architecture can handle multiple features and complex navigation flows.
Maintainability:
Encapsulated logic makes it easier to debug and test individual features.
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
Get the latest Android development articles delivered to your inbox.