Compose Navigation - navigation destination ... is not a direct child of this NavGraph
O

2

8

I am trying to build below navigation for my order management app:

manage_orders/manage_orders/{locationId}
manage_orders/manage_order_details/{orderId}

And here is my navigation code for that:

internal sealed class Screen(val route: String) {
    object ManageOrders : Screen(manage_orders)
}

private sealed class LeafScreen(val route: String) {

    fun createRoute(root: Screen): String {
        return "${root.route}/$route"
    }

    object ManageOrders : LeafScreen("manage_orders/{locationId}") {

        fun createRoute(root: Screen, locationId: String): String {
            return "${root.route}/manage_orders/$locationId"
        }
    }

    object ManageOrderDetails : LeafScreen("manage_order_details/{orderId}") {

        fun createRoute(root: Screen, orderId: String): String {
            return "${root.route}/manage_order_details/$orderId"
        }
    }
}

@ExperimentalCoroutinesApi
@Composable
internal fun AppNavigation(
    navController: NavHostController,
    locationId: String,
    modifier: Modifier = Modifier
) {
    NavHost(
        navController = navController,
        startDestination = Screen.ManageOrders.route,
        modifier = modifier,
    ) {
        addManageOrdersTopLevel(navController, locationId)
    }
}

@ExperimentalCoroutinesApi
private fun NavGraphBuilder.addManageOrdersTopLevel(
    navController: NavHostController,
    locationId: String
) {
    navigation(
        route = Screen.ManageOrders.route,
        startDestination = LeafScreen.ManageOrders.createRoute(Screen.ManageOrders, locationId)
    ) {
        addManageOrders(navController = navController, root = Screen.ManageOrders)
        addManageOrderDetails(navController = navController, root = Screen.ManageOrders)
    }
}

@ExperimentalCoroutinesApi
private fun NavGraphBuilder.addManageOrders(
    navController: NavHostController,
    root: Screen
) {
    composable(
        route = LeafScreen.ManageOrders.createRoute(root),
        arguments = listOf(
            navArgument(LOCATION_ID) { type = NavType.StringType }
        )
    ) { backStackEntry ->
        backStackEntry.arguments?.let {
            ManageOrders(locationId = it.getString(LOCATION_ID)!!) { orderId ->
                navController.navigate(LeafScreen.ManageOrderDetails.createRoute(root, orderId))
            }
        }
    }
}

@ExperimentalCoroutinesApi
private fun NavGraphBuilder.addManageOrderDetails(
    navController: NavHostController,
    root: Screen
) {
    composable(
        route = LeafScreen.ManageOrderDetails.createRoute(root),
        arguments = listOf(
            navArgument(ORDER_ID) { type = NavType.StringType }
        )
    ) { backStackEntry ->
        backStackEntry.arguments?.let {
            ManageOrderDetails(
                navController = navController,
                orderId = it.getString(ORDER_ID)
            )
        }
    }
}

And here is the code to start the navigation:

class ManageOrderActivity : AppCompatActivity() {

    @ExperimentalCoroutinesApi
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val navController = rememberNavController()
            AppNavigation(
                navController = navController,
                locationId = intent.extras?.getString(KEY_LOCATION_ID) ?: ""
            )
        }
    }
}

However, I am getting below error:

FATAL EXCEPTION: main
Process: io.chanse.locals.cerve.qa, PID: 18285
java.lang.IllegalArgumentException: navigation destination -1881727488 is not a direct child of this NavGraph
    at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.kt:72)
    at androidx.navigation.NavGraphNavigator.navigate(NavGraphNavigator.kt:49)
    at androidx.navigation.NavController.navigateInternal(NavController.kt:189)
    at androidx.navigation.NavController.navigate(NavController.kt:1491)
    at androidx.navigation.NavController.onGraphCreated(NavController.kt:913)
    at androidx.navigation.NavController.setGraph(NavController.kt:852)
    at androidx.navigation.NavController.setGraph(NavController.kt:90)
    at androidx.navigation.compose.NavHostKt$NavHost$4.invoke(NavHost.kt:113)
    at androidx.navigation.compose.NavHostKt$NavHost$4.invoke(NavHost.kt:112)
    at androidx.compose.runtime.DisposableEffectImpl.onRemembered(Effects.kt:81)
    at androidx.compose.runtime.CompositionImpl$RememberEventDispatcher.dispatchRememberObservers(Composition.kt:781)
    at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:639)
    at androidx.compose.runtime.Recomposer.composeInitial$runtime_release(Recomposer.kt:733)
    at androidx.compose.runtime.CompositionImpl.setContent(Composition.kt:432)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:144)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:135)
    at androidx.compose.ui.platform.AndroidComposeView.setOnViewTreeOwnersAvailable(AndroidComposeView.android.kt:727)
    at androidx.compose.ui.platform.WrappedComposition.setContent(Wrapper.android.kt:135)
    at androidx.compose.ui.platform.WrappedComposition.onStateChanged(Wrapper.android.kt:187)
    at androidx.lifecycle.LifecycleRegistry$ObserverWithState.dispatchEvent(LifecycleRegistry.java:354)
    at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.java:196)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:142)
    at androidx.compose.ui.platform.WrappedComposition$setContent$1.invoke(Wrapper.android.kt:135)
    at androidx.compose.ui.platform.AndroidComposeView.onAttachedToWindow(AndroidComposeView.android.kt:814)
    at android.view.View.dispatchAttachedToWindow(View.java:22010)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:4291)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:4298)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:4298)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:4298)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:4298)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:4298)
    at android.view.ViewGroup.dispatchAttachedToWindow(ViewGroup.java:4298)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3135)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2618)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9971)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1010)
    at android.view.Choreographer.doCallbacks(Choreographer.java:809)
    at android.view.Choreographer.doFrame(Choreographer.java:744)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:995)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:246)
    at android.app.ActivityThread.main(ActivityThread.java:8512)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1139)

What's the problem here?

Update 1 (as per Ian's solution)

@Composable
internal fun AppNavigation(
    navController: NavHostController,
    locationId: String,
    modifier: Modifier = Modifier
) {
    NavHost(
        navController = navController,
        startDestination = LeafScreen.ManageOrders.route,
        modifier = modifier,
    ) {
        addManageOrdersTopLevel(navController, locationId)
    }
}

@ExperimentalCoroutinesApi
private fun NavGraphBuilder.addManageOrdersTopLevel(
    navController: NavHostController,
    locationId: String
) {
    navigation(
        route = Screen.ManageOrders.route,
        startDestination = LeafScreen.ManageOrders.createRoute(Screen.ManageOrders)
    ) {
        addManageOrders(
            navController = navController,
            root = Screen.ManageOrders,
            locationId = locationId
        )
    }
}

private fun NavGraphBuilder.addManageOrders(
    navController: NavHostController,
    root: Screen,
    locationId: String
) {
    composable(
        route = LeafScreen.ManageOrders.createRoute(root),
        arguments = listOf(
            navArgument(LOCATION_ID) {
                type = NavType.StringType
                defaultValue = locationId
            }
        )
    ) { backStackEntry ->
        backStackEntry.arguments?.let {
            ManageOrders(locationId = it.getString(LOCATION_ID)!!) { orderId ->
                navController.navigate(LeafScreen.ManageOrderDetails.createRoute(root, orderId))
            }
        }
    }
}

But still facing the same issue. Look's like I failed to understand what Ian sggested. What have I missed?

Offside answered 3/9, 2021 at 1:52 Comment(1)
I'm facing the same issue, trying to pass default argument as Ian suggested, Still i'm getting same error, did you find any solution for thisHoule
C
7

This line:

startDestination = LeafScreen.ManageOrders.createRoute(Screen.ManageOrders, locationId)

Does not match any of the route parameters on your destination. For example, your Screen.ManageOrders's route is:

route = LeafScreen.ManageOrders.createRoute(root)

The startDestination needs to match a route exactly. That means you need to be using

startDestination = LeafScreen.ManageOrders.createRoute(root)

If you want to set a locationId to be used for your start destination, you should set a defaultValue on your argument:

@ExperimentalCoroutinesApi
private fun NavGraphBuilder.addManageOrdersTopLevel(
    navController: NavHostController,
    locationId: String
) {
    navigation(
        route = Screen.ManageOrders.route,
        startDestination = LeafScreen.ManageOrders.createRoute(root)
    ) {
        addManageOrders(
            navController = navController,
            root = Screen.ManageOrders,
            locationId = locationId
        )
        addManageOrderDetails(navController = navController, root = Screen.ManageOrders)
    }
}

@ExperimentalCoroutinesApi
private fun NavGraphBuilder.addManageOrders(
    navController: NavHostController,
    root: Screen,
    locationId: String
) {
    composable(
        route = LeafScreen.ManageOrders.createRoute(root),
        arguments = listOf(
            navArgument(LOCATION_ID) {
                type = NavType.StringType
                defaultValue = locationId
            }
        )
    ) { backStackEntry ->
        backStackEntry.arguments?.let {
            ManageOrders(locationId = it.getString(LOCATION_ID)!!) { orderId ->
                navController.navigate(LeafScreen.ManageOrderDetails.createRoute(root, orderId))
            }
        }
    }
}
Carolinecarolingian answered 3/9, 2021 at 2:14 Comment(5)
Still facing the same problem. Not sure why. Can you please look at my Update 1 section?Offside
You're still using LeafScreen.ManageOrders.route for your NavHost's startDestination, but Screen.ManageOrders.route for your route in addManageOrdersTopLevel. Your heavy use of constants built on constants built on constants I think is hurting you way more than it is helping you TBH.Carolinecarolingian
Thanks a lot for patiently helping me. It worked now. Moreover, i will remove all the constants as it complexifies the code. However, I still have a doubt regarding the documentation developer.android.com/jetpack/compose/navigation#nested-nav . In this section NavHost's startDetsination is startRoute and navigation's route is nestedand they are different. Am I misunderstanding this completely?Offside
I wouldn't say you'd need to go as far as to remove all constants (the route variable does seem useful), but I'd concentrate on the minimal set of constants you need. In that documentation example, the nested graph is not the start destination of the NavHost - the ... indicates that there's other destinations in the graph, presumably one with a route of startRoute.Carolinecarolingian
I tried both path and optional query parameters (the latter in combination with defaultValue). I posted a SO question here: https://mcmap.net/q/436977/-jetpack-compose-navigation-pass-argument-to-startdestination/2011622 It includes a link to a GitHub repo with a reproducable minimal sample app. I'd be happy if you took a look at this @CarolinecarolingianIneffable
M
-2

In my case the problem was I didn't include the route inside my NavGraphBuilder->

Before->

KoinNav(navController) {
        AnimatedNavHost(
            navController,
            startDestination =
            MainScreenItems.ContactListScreenItem.route
        ) {

  composable(route = Item.A.route) {
      contentA()
  }
  composable(route = Item.A.route) {
    contentB()
  }
}

After->

KoinNav(navController) {
        AnimatedNavHost(
            navController,
            startDestination =
            MainScreenItems.ContactListScreenItem.route
        ) {

  composable(route = Item.A.route) {
    contentA()
  }
  composable(route = Item.B.route) {
    contentB()
  }
}
Maroc answered 15/9, 2022 at 11:0 Comment(2)
Your before and after appear identical.Everara
route = Item.B.routeMaroc

© 2022 - 2024 — McMap. All rights reserved.