Observe MutableStateFlow anywhere

Sorry, can’t find the topic: how to observe MutableStateFlow changes in any class? Trying this but collect / onEach looks not legit

  @Test
  fun flowTest() {
      val scope = CoroutineScope(Dispatchers.Default)
      val state = MutableStateFlow(1)

      scope.launch {
          state.collect { // The code blocks here.
              println(it)
          }
      }

      state.value++
  }

And is it fine to use MutableStateFlow here if its value is also used in @Composable or something better is possible?

I’m not sure what do you mean and your code sample doesn’t help me to understand.

Flows are general-purpose tools like arrays - you can use them wherever and however you need. And yes, you can collect a flow in multiple places at the same time.

I want to perform some actions after MutableStateFlow value changes. Within my code it’s println(it) and this code became blocked on the collect method (while it works if state is Flow, not MutableStateFlow). So, I need to fix it. Maybe I need to add something like emit or use another method instead of collect?

Could you provide a code that works and one that doesn’t work? What do you mean by saying “code became blocked on the collect method”? That the execution waits at collect() and doesn’t go the the next line (which is expected) or that println() inside collect {} is not invoked?

Regarding your above example, you launch collect() in the background, so this code most probably executes after flowTest() already finishes. Although, I’m not exactly sure what problem this example was supposed to show.

This code works (prints output as needed):

@Test
fun flowTest() {
    val scope = CoroutineScope(Dispatchers.Default)

    val makeFlow = flow {
        println("sending first value")
        emit(1)
        println("first value collected, sending another value")
        emit(2)
        println("second value collected, sending a third value")
        emit(3)
        println("done")
    }

    scope.launch {
        makeFlow.collect { value ->
            println("got $value")
        }
        println("flow is completed")
    }
}

The code from the first message doesn’t (no print at all, execution halted).

collect() always awaits for the flow to complete and state flows never complete. Therefore, it is expected that it “blocks” for MutableStateFlow.

For the problem about missing println(it) you need to be aware that you run collect() concurrently to flowTest(), so it is not at all guaranteed what will be the order of operations here. I would expect that in most cases collect() will be invoked long after flowTest() finished executing. So depending on how do you run tests, output of this println() may be not associated with flowTest() or the process may end before collect() even starts executing. However, I can’t explain why you see different results in both cases. When I tested this locally, I didn’t see the output in both cases - I had to add Thread.sleep() inside flowTest(), so collect() had a chance to execute.

1 Like

It’s not only within a test, I tried to run in the main app and the runtime halted with the .collect. What is the right method to observe MutableStateFlow changes? Could you write a sample code?

collect() is the right way of observing MutableStateFlow for changes. You just need to allocate some coroutine to make this observing. And that means that yes, it will wait at collect() until you no longer need to observe it, so you then cancel it.

Usually, if you have some kind of a component with a specific lifetime (some service, UI page, etc.), then it has a coroutine scope that lives as long as that component lives (e.g. lifecycleScope in Android). If this component needs to observe a MutableStateFlow for changes, it uses its scope to launch() collecting of a flow. This way it observes all the time and automatically stops observing when the component is not needed anymore.