The app I'm building uses compose navigation with routes. The challenge is that the start destination is dynamic.
Here is a minimal example:
class MainActivity : ComponentActivity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "dynamic/1", // doesn't work
// startDestination = "static", // workaround
) {
composable(
route = "dynamic/{$ARG_ID}",
arguments = listOf(navArgument(ARG_ID) { type = NavType.StringType }),
) {
val id = it.arguments?.getString(ARG_ID)
Text("dynamic route, received argument: $id!")
}
// part of the workaround
// composable(
// route = "static",
// ) {
// LaunchedEffect(this) {
// navController.navigate("dynamic/1")
// }
// }
}
}
}
companion object
{
const val ARG_ID = "id"
}
}
The app crashes with
java.lang.IllegalArgumentException: navigation destination route/1 is not a direct child of this NavGraph
The problem only exists if the "dynamic" route is used as start destination. This can be verified by using startDestination = "static"
.
Although, the "static" route workaround works I'm looking for a solution without it because it kind of obfuscates the code and also creates an additional entry in the back stack.
-> Full code sample to reproduce the issue
Related SO questions
- Navigation Architecture Component- Passing argument data to the startDestination - Answers don't seem to be applicable to Compose Navigation.
- Pass an argument to a nested navigation graph in Jetpack Compose - No answer given.
- Compose Navigation - navigation destination ... is not a direct child of this NavGraph - The accepted answer doesn't resolve the issue.
Edit:
I want to stress that the original sample used to not contain the "static" composable. I only added the "static" composable to have a working startDestination
and to prove that the "dynamic" composable can be navigated to.
Update:
Even switching to the query parameter syntax for optional arguments, providing a default value, and setting the start destination without any argument does not work.
The following variation
NavHost(
navController = navController,
startDestination = "dynamic",
) {
composable(
route = "dynamic?$ARG_ID={$ARG_ID}",
arguments = listOf(navArgument(ARG_ID) { type = NavType.StringType; defaultValue = "1" }),
) {
val id = it.arguments?.getString(ARG_ID)
Text("dynamic route, received argument: $id!")
}
}
Leads to the exception
java.lang.IllegalArgumentException: navigation destination dynamic is not a direct child of this NavGraph
static
callnavController.navigate("dynamic/1")
, just have it call your real content composable for the dynamic scenario, passing in"1"
as a hard-coded value, rather than getting it from the argumentsBundle
. That avoids the additional entry in the back stack, and it would not seem to be especially complex. – AdriannaadriannenavController.navigate("dynamic/1")
and what you would set asstartDestination
? – Norwordstatic
route, but have it call the same function that yourdynamic
route does, just with a hard-coded"1"
parameter, rather than getting the value from the arguments bundle. – AdriannaadriannepopUpTo()
and for the decision which bottom bar item needs to be marked selected given the current route. – NorworddefaultValue
on yourdynamic
argument, to perhaps avoid setting a value when it is your start destination? That might require you to make it an optional argument. – AdriannaadriannestartDestination
if I remove any arguments from thecomposable
route
but then the route can't be navigated to with arguments - all call-side arguments are ignored if only declared in thearguments
list but not mentioned in the route. – Norword"dynamic"
you use for yourstartDestination
needs to match, character by character, the exact string you use in yourcomposable
route
: it needs to be"dynamic?$ARG_ID={$ARG_ID}"
. – PlectrumstartDestination
needed to be a concrete route (containing the arguments' values). Wouldn't it be a cool feature if that was possible? Then, arguments wouldn't require a default value and could be declared as truly mandatory. – Norword