How to handle one-shot operations in Jetpack Compose?
I prefer using SharedFlow
for a job like this:
class OneShotOperationViewModel : ViewModel() {
private val _toastMessage = MutableSharedFlow<String>()
val toastMessage = _toastMessage.asSharedFlow()
fun sendMessage(message: String) {
viewModelScope.launch {
_toastMessage.emit(message)
}
}
}
@Composable
fun TestScreen() {
val context = LocalContext.current
val viewModel = viewModel<OneShotOperationViewModel>()
LaunchedEffect(Unit) {
viewModel
.toastMessage
.collect { message ->
Toast.makeText(
context,
message,
Toast.LENGTH_SHORT,
).show()
}
}
Button(
onClick = {
viewModel.sendMessage("Sample Toast")
},
) {
Text(text = "Show Toast")
}
}
Note that if you send a message from another view while collect
is not running, it will not show toast when you finally start it. It does not store the value, it only passes it on to all the collectors that are currently connected.
How to use SharedFlow in Jetpack Compose
Technically you can collect it as state as any other Flow
- with an initial value:
flow.collectAsState(initial = 0)
This state will have the last value emitted by the flow during the time of view being presented, or the initial value. I'm not sure this makes much sense, though.
But you can also use it as a way to deliver events that require a one-time response, as shown in this answer.
correct way to handle mutable state of list of data in jetpack compose
You have two options here:
Declare your list as
mutableStateListOf
. In this case you can do any operations on it, likeclear()
,add()
,remove()
. See this answer how you can use it withrememberSaveable
, or move it to the view model.val data = remember {
mutableStateListOf<Int>()
}
data.clear()Sometimes it's more comfortable to use
mutableStateOf
, in this case you can update it value like this:var data by rememberSaveable(Unit) {
mutableStateOf(listOf<Int>())
}
data = data.toMutableList().apply {
clear()
}.toImmutableList()
Compose side effects + Jetpack navigation + onBackPressed = Stuck navigation
I use SharedFlow for emitting one-time events like this
class MyViewModel : ViewModel() {
private val _eventFlow = MutableSharedFlow<OneTimeEvent>()
val eventFlow = _eventFlow.asSharedFlow()
private fun emitEvent(event: OneTimeEvent) {
viewModelScope.launch { _eventFlow.emit(event) }
}
}
The sealed class for defining events
sealed class OneTimeEvent {
object SomeEvent: OneTimeEvent()
}
And finally in the Screen collect the flow like this
fun MyScreen(viewModel: MyViewModel = hiltViewModel()) {
LaunchedEffect(Unit) {
viewModel.eventFlow.collect { event ->
when(event){
SomeEvent -> {
//Do Something
}
}
}
}
}
So whenever your state changes you can emit some event and take action against it in your Screen.
Composable is recomposing endlessly after flow collect
During navigation transition recomposition happens multiple times because of animations, and you call navController.navigate
on each recomposition.
You should not cause side effects or change the state directly from the composable builder, because this will be performed on each recomposition, which is not expected in cases like animation.
Instead you should use side effects. In your case, LaunchedEffect
should be used.
if (response.data) {
LaunchedEffect(Unit) {
Log.i("lolipop", "lolipopi")
navController.navigate(Screen.SignUpConfirmation.route)
}
}
Related Topics
Display Fb Profile Pic in Circular Image View in Application
Display a Part of the Webpage on the Webview Android
Why Can't One Add/Remove Items from an Arrayadapter
Single Click and Double Click of a Button in Android
Android: How to Change the Datepicker View Date Format from Mm/Dd/Yyyy to Dd/Mm/Yyyy
Out of Memory Exception Due to Large Bitmap Size
How to Show Daily Offline Notifications in Android 10
How to Display Map (Google) on a Phonegap Android Application
Live-Stream Video from One Android Phone to Another Over Wifi
Differencebetween Sendstickybroadcast and Sendbroadcast in Android
How to Stop an Animation (Cancel() Does Not Work)
Scale Fit Mobile Web Content Using Viewport Meta Tag
Recompile with -Xlint in Android Studio
Running Emulator After Building Android from Source
Android Audiorecord Supported Sampling Rates
Android Opening a File with Action_Get_Content Results into Different Uri'S