Reputation: 3192
I have a setup where I want to navigate to a composable for result and then depending on that result I want to launch another composable from the parent. Here is the setup
ScreenA launches Screen B for result (user does something in screenB) -> ScreenB sets a result and pops itself from the stack -> ScreenA looks at the result and depending on the value launches ScreenC.
The issue here is that the observer I have set on the result keeps firing off infinitely once the result is set by ScreenB.
Here is a simplified version of my setup:
fun AppNavHost(
navController: NavHostController
) {
NavHost(
navController = navController,
startDestination = "ScreenA",
) {
composable("ScreenA") {
ScreenA(
onClick = {
navController.navigate("ScreenB")
}
)
val result = navController.currentBackStackEntry
?.savedStateHandle
?.getLiveData<String>("key")
?.observeAsState()
result?.value?.let { str ->
// This seems to not remove the key or at least
// the live data value is not nulled out
navController.currentBackStackEntry
?.savedStateHandle
?.remove<String>("key")
if (str == "result") {
// This condition hits infinitely
navController.navigate("ScreenC")
// This logs infinitely
Log.d("TAG", "found result: $str")
}
}
}
composable("ScreenB") {
ScreenB(
onClick = {
navController.previousBackStackEntry
?.savedStateHandle
?.set("key", "result")
navController.popBackStack()
}
)
}
composable("ScreenC") { ScreenC() }
}
}
I have already looked at this question and my setup here is similar. How can I fix this behavior so the result of ScreenB is only read once?
Upvotes: 1
Views: 1539
Reputation: 405
Use LaunchedEffect to avoid multiple calls to navigate at the recomposition level.
NavHost(
navController = navController,
startDestination = "ScreenA",
) {
composable("ScreenA") {
Button(
onClick = {
navController.navigate("ScreenB")
},
modifier = Modifier.padding(16.dp)
) {
Text("Go to Screen B")
}
val result = navController.currentBackStackEntry
?.savedStateHandle
?.getLiveData<String>("key")
?.observeAsState()
result?.value?.let { str ->
navController.currentBackStackEntry
?.savedStateHandle
?.remove<String>("key")
LaunchedEffect(str) {
if (str == "result") {
navController.navigate("ScreenC")
Log.d("TAG", "found result: $str")
}
}
}
}
composable("ScreenB") {
Button(
onClick = {
navController.previousBackStackEntry
?.savedStateHandle
?.set("key", "result")
navController.popBackStack()
}
) {
Text("Screen B")
}
}
composable("ScreenC") { ScreenC() }
}
Upvotes: 4