Navigation in Compose: From Basic Routes to Complex Flows
NavigationArchitectureDeep LinksMulti-module
Navigation Compose Fundamentals
Compose Navigation moves away from fragment transactions to a declarative model where destinations are composables. The core components are NavHost, NavController, and NavGraph.
@Composable
fun AppNavigation() {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable("home") {
HomeScreen(
onNavigateToDetail = { id ->
navController.navigate("detail/$id")
}
)
}
composable(
route = "detail/{itemId}",
arguments = listOf(
navArgument("itemId") { type = NavType.StringType }
)
) { backStackEntry ->
val itemId = backStackEntry.arguments?.getString("itemId")
DetailScreen(itemId = itemId)
}
}
}Type-Safe Navigation with Kotlin Serialization
String-based routes are error-prone. Use sealed classes or data classes with Kotlin Serialization for type-safe navigation:
@Serializable
sealed class Screen {
@Serializable
data object Home : Screen()
@Serializable
data class Detail(val itemId: String) : Screen()
@Serializable
data class Profile(val userId: String, val showEdit: Boolean = false) : Screen()
}
// Usage with type-safe navigation
navController.navigate(Screen.Detail(itemId = "123"))
navController.navigate(Screen.Profile(userId = "456", showEdit = true))Nested Navigation Graphs
For complex apps, organize related destinations into nested graphs. This improves code organization and enables modular navigation:
NavHost(navController = navController, startDestination = "main") {
// Main flow
navigation(startDestination = "home", route = "main") {
composable("home") { HomeScreen() }
composable("search") { SearchScreen() }
}
// Auth flow - separate graph
navigation(startDestination = "login", route = "auth") {
composable("login") { LoginScreen() }
composable("register") { RegisterScreen() }
composable("forgot_password") { ForgotPasswordScreen() }
}
// Settings flow
navigation(startDestination = "settings_main", route = "settings") {
composable("settings_main") { SettingsScreen() }
composable("settings_account") { AccountSettingsScreen() }
}
}Deep Links Integration
Deep links allow external sources to navigate directly to specific screens. Configure them in your composable destinations:
composable(
route = "product/{productId}",
arguments = listOf(navArgument("productId") { type = NavType.StringType }),
deepLinks = listOf(
navDeepLink { uriPattern = "https://myapp.com/product/{productId}" },
navDeepLink { uriPattern = "myapp://product/{productId}" }
)
) { backStackEntry ->
ProductScreen(productId = backStackEntry.arguments?.getString("productId"))
}Note: Remember to declare deep link intents in AndroidManifest.xml for the app to respond to external URLs.