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...
- Start the app fresh - i'm at home
- Navigate to Calendar with argument
A
- it showsA
- Navigate to Home via BottomBar
- Navigate to Calendar with argument
B
- it showsB
- Navigate to More
- Navigate to Calendar via BottomBar - it shows
A
notB
- 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:
- Calendar will always display an argument when explicitly provided
- Calendar will display none or the last argument, when no argument is provided
- "Back" works correctly: Navigating back from Calendar / More should lead to Home
- State (e.g. scroll state) is kept as you'd expect
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) },
)
}
}
}