Lazy MutableStateFlow

I need to change state in @Composable without its recomposition.

val state by viewModel.state.collectAsState()

LaunchedEffect(currentValue) {
    if (currentValue == Done) viewModel.state.value = null
}

The way I thought about is something like to split variables:

val state = Done
val stateFlow by MutableStateFlow(state)

But not sure it should work this way. So, what the current methods to solve the problem? Have no answers from SOF.

I’m pretty new to Jetpack Compose myself, but I’m pretty sure that’s specifically going against the design of Jetpack Compose. State changes are meant to trigger a recompose. If you want state that doesn’t trigger a recompose, I think you just use it as a normal variable and don’t register it inside Jetpack Compose (IE don’t use mutableStateOf { ... } or whatever).

Also this may help: https://www.jetpackcompose.net/state-in-jetpack-compose

Dunno if you’ve seen these; for whatever reason, I found it very hard to find this site when I was getting into Jetpack Compose, but it’s got so much useful information!

3 Likes

The main goal is to show BottomSheet only when the item is selected.

You’re gonna have to provide me with more info, like samples of your code or something. I don’t know what BottomSheet is, or what items are being selected.

Sorry, it’s from Android. The original question is here. It’s big, so I shouldn’t copy it here.

Sorry, I don’t know enough about Android or Jetpack Compose to help from that amount of code. If you’ve got a project I can download and tinker with I might be able to help. The only thing that did seem odd at a glance was this:

LaunchedEffect(bottomSheetState.currentValue) {
    if (bottomSheetState.currentValue == SheetValue.Hidden ) {
        viewModel.selectItem(null)
    }
}

if (selectedItem == null) {
    LaunchedEffect(bottomSheetState.currentValue) { bottomSheetState.hide() }
}

I don’t know exactly what LaunchedEffect is, but I’m guessing it’s some kind of coroutine thing that uses launch and asynchronously watches the value of a state variable, and triggers the lambda when the state changes? It seems like the way this code works, when it executes, the first LaunchedEffect will be fired, and if the sheet state is hidden, it nulls out the view model selected item. If it doesn’t suspend, that’ll execute before the next bit of code, which uses another LaunchedEffect (since selectedItem would be null) and hides the bottom sheet when the state changes. Or it might be the opposite, it registers the next LaunchedEffect, the first one runs and nulls out the selectItem, which triggers the nest one, which hides the sheet? Idk, it just looks to me like using two LaunchedEffects on the same value might be triggering some kind of recompose loop, where one effect modifies state, which triggers a recompose, and then the other effect fires and also triggers a recompose.

Yes, LaunchedEffect is a coroutine that relaunches after its key changed
LaunchedEffect(key){...}

I don’t really have time to debug this right now, but I think it’s due to you registering multiple listeners on a single value change; you probably need to rethink how you’re doing it. I think the issue is that one state change triggers a recompose and calls one listener, that listener updates something which causes another recompose, and then triggers another listener, and so you end up with multiple recomposes instead of just one.