How to Handle One-Shot Operations in Jetpack Compose

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:

  1. Declare your list as mutableStateListOf. In this case you can do any operations on it, like clear(), add(), remove(). See this answer how you can use it with rememberSaveable, or move it to the view model.

    val data = remember {
    mutableStateListOf<Int>()
    }
    data.clear()
  2. 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



Leave a reply



Submit