My current Android Jetpack Compose application employs snapShotFlow
to convert mutableStateOf()
to flow and trigger user actions as follows
In ViewModel:-
var displayItemState by mutableStateOf(DisplayItemState())
@Immutable
data class DisplayItemState(
val viewIntent: Intent? = null
)
In composable:-
val displayItemState = viewModel.displayItemState
LaunchedEffect(key1 = displayItemState) {
snapshotFlow { displayItemState }
.distinctUntilChanged()
.filter { it.viewIntent != null }
.collectLatest { displayItemState ->
context.startActivity(displayItemState.viewIntent)
}
}
everything works as expected while I keep my test device in portrait or landscape.
However when I change the device orientation the last collected snapShotFlow
value is resent.
If I reset the displayItemState as follows in the snapShotFlow this fixes the issue however this feels like the wrong fix. What am i doing wrong? what is the correct approach to stop the snapShotFlow from re triggering on orientation change
val displayItemState = viewModel.displayItemState
LaunchedEffect(key1 = displayItemState) {
snapshotFlow { displayItemState }
.distinctUntilChanged()
.filter { it.viewIntent != null }
.collectLatest { displayItemState ->
context.startActivity(displayItemState.viewIntent)
viewModel.displayItemState = DisplayItemState()
}
}
CodePudding user response:
That's intended behavior, you are not doing anything wrong. Compose's (Mutable)State
holds the last value, similarly to StateFlow
, so new collection from them always starts with the last value.
Your solution is ok, something very similar is actually recommended in Android's app architecture guide here:
For example, when showing transient messages on the screen to let the user know that something happened, the UI needs to notify the ViewModel to trigger another state update when the message has been shown on the screen.
Another possibility would be to use SharedFlow
instead of MutableState
in your viewModel - SharedFlow
doesn't keep the last value so there won't be this problem.