This episode unifies screens into an application: Navigation Compose with NavHost and the nav graph, passing arguments, deep links, Single Activity architecture, and modular routing that scales easily.

Real applications have many screens: home, detail, profile, checkout. Episode 8 connects them all with Navigation Compose, the official library for navigating between composables.
With the Single Activity architecture, every screen is a composable inside a single Activity. Navigation becomes a movement between routes in a nav graph — no longer a collection of Activities calling each other like the old approach.
Episode 8 covers NavHost and the nav graph, passing arguments, deep links, and modular routing patterns for applications at scale.
Navigation Compose is added as a separate dependency:
implementation("androidx.navigation:navigation-compose:2.8.9")androidx.navigation:navigation-compose provides NavHost, NavController, and all the navigation APIs. The AndroidX BOM version catalog doesn't include navigation, so the version is written explicitly.
NavController is the core of navigation, and NavHost maps routes to composables:
@Composable
fun AppNavHost() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "beranda"
) {
composable("beranda") {
BerandaScreen(onBukaDetail = { id ->
navController.navigate("detail/$id")
})
}
composable("detail/{id}") { backStackEntry ->
val id = backStackEntry.arguments
?.getString("id") ?: ""
DetailScreen(id)
}
}
}composable("beranda") and composable("detail/{id}") define routes. Navigation is done with navController.navigate("detail/$id") — the navigation key is a route string.
Route parameters can be assigned a type with navArgument:
composable(
route = "detail/{userId}",
arguments = listOf(navArgument("userId") { type = NavType.IntType })
) { backStackEntry ->
val userId = backStackEntry.arguments?.getInt("userId") ?: 0
DetailScreen(userId)
}navArgument("userId") { type = NavType.IntType } makes the argument read as an Int, not a String. Argument parsing is handled by Navigation Compose, and valid values are automatic.
Deep links allow a route to be opened from outside the application — from a notification or a URL:
composable(
route = "promo/{kode}",
deepLinks = listOf(
navDeepLink("https://devnull.id/promo/{kode}")
)
) { backStackEntry ->
val kode = backStackEntry.arguments?.getString("kode") ?: ""
PromoScreen(kode)
}navDeepLink("https://devnull.id/promo/{kode}") maps a URL to a route. Opening that URL navigates straight to PromoScreen. You can test a deep link from the terminal with adb shell am start -a android.intent.action.VIEW -d "https://devnull.id/promo/HEMAT2026".
The old approach created one Activity per screen and built communication between Activities. In the Single Activity architecture, there is only one Activity acting as a host; every screen is a composable. The benefits: smooth transitions between screens, easier state management, and a simpler lifecycle.
Navigation state is stored in NavController, not in each screen. This means per-screen state (from episode 5) must be designed to be restorable — rememberSaveable and SavedStateHandle in the ViewModel will be used in episode 11.
As the application grows, one big NavHost is hard to maintain. Split the routes into several NavGraphBuilder extensions per feature and combine them at the root:
fun NavGraphBuilder.fiturAuth(navController: NavController) {
composable("login") { LoginScreen(
onSukses = { navController.navigate("beranda") }
) }
composable("register") { RegisterScreen() }
}
@Composable
fun RootNavHost() {
val navController = rememberNavController()
NavHost(navController, startDestination = "login") {
fiturAuth(navController)
fiturBeranda(navController)
fiturProfil(navController)
}
}fiturAuth, fiturBeranda, and fiturProfil are NavGraphBuilder extensions that separate routes per feature. This is the basic pattern for modularization — you can start thinking about separate Gradle modules in episode 20.
Episode 8 connects screens into an application: NavHost and NavController, typed arguments with navArgument, deep links that open routes from outside, Single Activity architecture, and splitting the nav graph per feature for applications at scale.
Key takeaways:
In episode 9 we will discuss lists and lazy UI — LazyColumn, LazyRow, and grid layouts, efficient item rendering, Paging 3 integration, and animations in lazy lists.