Compose navigation
Skill almasumdev/awesome-kotlin-android-agent-skills/.github/skills/ui/compose-navigation
Expert guidance on Compose Navigation using typed routes with kotlinx.serialization, NavHost setup, deep links, and navigation-scoped ViewModels. Use this for any navigation task.From its SKILL.md
npx -y skills add almasumdev/awesome-kotlin-android-agent-skills --skill compose-navigationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.6 KB, ~1.0k tokens by cl100k_base, as published. Nobody here has run it
Compose Navigation with Typed Routes
Instructions
Use androidx.navigation:navigation-compose:2.8+ with type-safe routes powered by kotlinx.serialization. String-based routes are legacy; do not introduce them in new code.
1. Define Routes as Serializable Objects
@Serializable data object HomeRoute
@Serializable data object SettingsRoute
@Serializable data class ArticleRoute(val id: String)
@Serializable data class SearchRoute(val query: String = "", val tag: String? = null)
Apply the kotlin.plugin.serialization Gradle plugin and depend on org.jetbrains.kotlinx:kotlinx-serialization-json.
2. NavHost
@Composable
fun AppNavHost(navController: NavHostController = rememberNavController()) {
NavHost(navController = navController, startDestination = HomeRoute) {
composable<HomeRoute> {
HomeRoute(
onOpenArticle = { id -> navController.navigate(ArticleRoute(id)) },
onOpenSettings = { navController.navigate(SettingsRoute) },
)
}
composable<ArticleRoute> { backStack ->
val args: ArticleRoute = backStack.toRoute()
ArticleRoute(id = args.id, onBack = navController::popBackStack)
}
composable<SearchRoute> { backStack ->
val args: SearchRoute = backStack.toRoute()
SearchRoute(initialQuery = args.query, initialTag = args.tag)
}
composable<SettingsRoute> { SettingsRoute() }
}
}
backStack.toRoute<T>() decodes the arguments type-safely.
3. Nested Graphs and Scopes
navigation<ProfileGraphRoute>(startDestination = ProfileOverviewRoute) {
composable<ProfileOverviewRoute> { /* ... */ }
composable<ProfileEditRoute> { /* ... */ }
}
Get a ViewModel scoped to the nested graph:
val parent = remember(backStack) { navController.getBackStackEntry(ProfileGraphRoute) }
val vm: ProfileGraphViewModel = hiltViewModel(parent)
4. Deep Links
composable<ArticleRoute>(
deepLinks = listOf(navDeepLink<ArticleRoute>(basePath = "https://example.com/a")),
) { /* ... */ }
Declare matching <intent-filter> in AndroidManifest.xml:
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https" android:host="example.com" android:pathPrefix="/a"/>
</intent-filter>
5. Passing Complex Arguments
Primitives and @Serializable classes work out of the box. For non-primitive types you don't own, provide a NavType:
val FilterNavType = object : NavType<Filter>(isNullableAllowed = false) {
override fun put(bundle: Bundle, key: String, value: Filter) { bundle.putString(key, Json.encodeToString(value)) }
override fun get(bundle: Bundle, key: String): Filter = Json.decodeFromString(bundle.getString(key)!!)
override fun parseValue(value: String): Filter = Json.decodeFromString(Uri.decode(value))
override fun serializeAsValue(value: Filter): String = Uri.encode(Json.encodeToString(value))
}
composable<SearchRoute>(typeMap = mapOf(typeOf<Filter>() to FilterNavType)) { /* ... */ }
6. Pop, Replace, Single-Top
navController.navigate(HomeRoute) {
popUpTo(LoginRoute) { inclusive = true }
launchSingleTop = true
}
7. Events from ViewModels
Do not hold a NavController in a ViewModel. Instead, expose events and let the composable layer navigate:
LaunchedEffect(Unit) {
vm.events.collect { e ->
when (e) {
is LoginEvent.Success -> navController.navigate(HomeRoute) {
popUpTo(LoginRoute) { inclusive = true }
}
}
}
}
8. Back Handling
BackHandler(enabled = state.hasUnsaved) { showLeaveDialog = true }
Checklist
- All routes are
@Serializableobjects/classes — no string route templates. -
kotlinx-serializationplugin applied in the module. -
NavHoststart destination is a route instance, not a string. - Deep-link domains are verified in
AndroidManifest.xmlwithandroid:autoVerify="true". - ViewModels signal navigation via events; composables call
navController. - Nested graphs use
getBackStackEntry(GraphRoute)for scopedhiltViewModel.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.