Until Navigation 2.8.0 (September 2024), a route was a string, shaped like a URL path, with arguments as {placeholders} declared separately. Most existing code still looks like this, so you need to read it:
NavHost(nav, startDestination = "catalog") {
composable("catalog") {
Column {
books.take(3).forEach { b ->
TextButton(onClick = { nav.navigate("book/${b.id}") }) { Text(b.title) }
}
}
}
composable("book/{id}", arguments = listOf(navArgument("id") { type = NavType.IntType })) {
val id = it.arguments?.getInt("id") // untyped: a Bundle lookup by name
Log.d("BookNestNav", "string route ${it.destination.route}, id=$id")
Text("Book $id")
}
}Tapping Salt and Saffron logged:
D/BookNestNav: string route book/{id}, id=3It works, but nothing checks it: a typo in "book/$id" or "id" compiles and fails at run time, and the type is declared in one place and read in another. Pass an ID, never the object: arguments are saved in the back stack's Bundle, and a large one can exceed the 1 MB binder limit. (The line was logged three times, once per composition, a reminder that a composable body is no place for side effects.)