Kotlin Jetpack Compose - Passing data arguments to navigation drawer

77 Views Asked by At

Complete beginner here, so forgive me if I'm missing something obvious.

I am trying to navigate with jetpack compose Modal Navigation Drawer, using a viewmodel to pull entries out of a table.

I have successfully set up a load screen to choose from the list of entries in the database and navigate to the next page; however, I can't work out how to save this ID for travelling between pages with the navigation drawer.

Main App

@Composable
fun MyApp(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberNavController(),
) {
    val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)

    ModalNavigationDrawer(
        drawerState = drawerState,
        modifier = Modifier,
        drawerContent = {
            ModalDrawerSheet(
                modifier = Modifier.fillMaxWidth(0.5f),
            ) {
                Text(
                    stringResource(R.string.meat_space),
                    textAlign = TextAlign.Center
                )
                Divider()

                NavDrawerItem(
                    label = FrontPageDestination.titleRes,
                    onClick = {
                        if (currentRoute != FrontPageDestination.route) {
                            navController.navigate(FrontPageDestination.route) {
                                launchSingleTop = true
                            }
                        }
                        coroutineScope.launch { drawerState.close() }
                    }
                )
                NavDrawerItem(
                    label = SecondPageDestination.titleRes,
                    onClick = {
                        navController.navigate(SecondPageDestination.route) {
                            coroutineScope.launch { drawerState.close() }
                        }
                    }
                )
//more navdrawer items to be added here
            }
        }
    ) {
        Scaffold(
            modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
            topBar = {
                CenterAlignedTopAppBar(
                    title = {
                        if (currentRoute != FrontPageDestination.route) {
                            Text(currentRoute.replaceFirstChar { it.uppercase() })
                        }
                    },
                    navigationIcon = {
                        if (currentRoute != FrontDestination.route) {
                            IconButton(onClick = {
                                coroutineScope.launch { drawerState.open() }
                            }) {
                                Icon(
                                    imageVector = Icons.Default.Menu,
                                    contentDescription = stringResource(R.string.open_navigation_drawer)
                                )
                            }
                        }
                    },
                    scrollBehavior = scrollBehavior,
                    modifier = Modifier,
                )
            },
        ) {
            Surface(
                modifier = Modifier
                    .fillMaxSize()
                    .padding(top = it.calculateTopPadding())
            ) {
                MyAppNavHost(navController = navController)
            }
        }
    }
}

with more pages to be added in drawer items that can be called with the ID

Then in the NavHost:


@Composable
fun MyAppNavHost(
    navController: NavHostController,
    modifier: Modifier = Modifier
) {
    NavHost(
        navController = navController,
        startDestination = FrontPageDestination.route,
        modifier = modifier
    ) {
        composable(FrontPageDestination.route) {
            FrontScreen(
                navigateToSecondPage = { navController.navigate("${SecondPageDestination.route}/${it}") }
            )
        }

        composable(
            route = SecondPageDestination.routeWithArgs,
            arguments = listOf(navArgument(SecondPageDestination.entryIdArg) {
                type = NavType.IntType
            })
        )
        {
            SecondPageScreen()
        }
        //composable...

    }
}

And finally, in the Front Page screen I have the load button which works successfully:

                LoadIdDialog(
                    onDismissRequest = {  },
                    idList = idList,
                    onIdClick = { onIdClick(it.id) },
                    modifier = Modifier
                )
            }
        }
    }
}

The entire page of code is quite long and links into the viewmodel, but I can post if needed

How do I navigate between Navigation Drawer pages while retaining the database id?

I tried

NavDrawerItem(
                    label = SecondPageDestination.titleRes,
                    onClick = {
                        navController.navigate(SecondPageDestination.routeWithArgs) {
                            coroutineScope.launch { drawerState.close() }
                        }
                    }
                )

and also

navController.navigate("${SecondPageDestination.route}/{$entryIdArg}")

But these both give the error

java.lang.IllegalArgumentException: Navigation destination that matches request NavDeepLinkRequest{ uri=android-app://androidx.navigation/secondpage/{entryId} } cannot be found in the navigation graph

I think if I could extract the entryId as its string value to put it back into the drawer navigation it might work? But not sure how to do that.

1

There are 1 best solutions below

1
Nadav On

It seems like the only way to do this that I can work out is to have the nav drawer composed with every separate page and each of those pages have nav functions to every other page on the list.

A bit clunky, and means recomposition of the nav drawer with every page but not sure how else to do it.