I have an Android app using compose navigation. Navigating between its three screens - Home, Calendar, More - is done via a bottom bar:
// onBottomBarItemClick from https://developer.android.com/jetpack/compose/navigation#bottom-nav:
navController.navigate(destination) {
popUpTo("HOME") {
saveState = true
}
launchSingleTop = true
restoreState = true
}
Up until now everything is working as expected.
However, i sometimes want to pass an argument from Home to Calendar - see screenshot.
This is where things start to break apart.
HomeScreen(
onNavigateToCalendar = { argument ->
navController.navigate("CALENDAR?ARG=$argument") {
popUpTo("HOME") {
saveState = true
}
launchSingleTop = true
restoreState = false
}
}
)
If i now do the following...
A - it shows AB - it shows BA not B - which is weird.I believe i have tried every possible combination of saveState / launchSingleTop / restoreState, but all of them had some issues. Can someone help me please? I'm loking for a solution where:

Minimal Example:
@Composable
fun MainScreen() {
val navController = rememberNavController()
Scaffold(
content = { paddingValues ->
NavHost(
navController = navController,
startDestination = "HOME",
modifier = Modifier.padding(paddingValues)
) {
composable("HOME") {
HomeScreen(
onNavigateToCalendar = { argument ->
navController.navigate("CALENDAR?ARG=$argument") {
popUpTo("HOME") {
saveState = true
}
launchSingleTop = true
restoreState = false
}
}
)
}
composable("CALENDAR?ARG={ARG}") {
CalendarScreen(it.arguments?.getString("ARG"))
}
composable("MORE") {
MoreScreen()
}
}
},
bottomBar = {
MyBottomBar(
onClick = { destination ->
navController.navigate(destination) {
popUpTo("HOME") {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
)
}
@Composable
private fun HomeScreen(
onNavigateToCalendar: (argument: String) -> Unit
) {
Column {
Text("HOME")
Button(
onClick = { onNavigateToCalendar("A") },
content = { Text("Navigate to CALENDAR with argument = A") },
)
Button(
onClick = { onNavigateToCalendar("B") },
content = { Text("Navigate to CALENDAR with argument = B") },
)
}
}
@Composable
private fun CalendarScreen(argument: String?) {
Text("CALENDAR with argument = $argument")
}
@Composable
private fun MoreScreen() {
Text("MORE")
}
@Composable
private fun MyBottomBar(
onClick: (destination: String) -> Unit
) {
BottomAppBar {
listOf("HOME", "CALENDAR", "MORE").forEach { destination ->
BottomNavigationItem(
selected = false, // TODO - not important for now
icon = {},
label = { Text(destination) },
onClick = { onClick(destination) },
)
}
}
}
When navigating to your Calendar screen sending a specific parameter (either A or B, via the onClick listener) you need to clear the backStack of your route so the new state can be saved correctly. Like this:
onNavigateToCalendar = { argument ->
navController.clearBackStack("CALENDAR?ARG={ARG}")
navController.navigate("CALENDAR?ARG=$argument") {
...
}
}
For more details you can check their official comment here: https://issuetracker.google.com/issues/294408574
To me it is implicit that I want to override the previous value by using savingState=true and restoreState=false, but seems they don't save any state if you didn't restored the previous one... so you need to clear the backstack manually before going to your screen
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With