Question Details

No question body available.

Tags

kotlin android-jetpack-compose

Answers (2)

Accepted Answer Available
Accepted Answer
July 23, 2025 Score: -1 Rep: 34 Quality: Medium Completeness: 40%

You don’t need to wrap it in viewModelScope.launch just to update a MutableStateFlow. If you’re already on the main thread (like from a Composable or button click), just assigning to _filter.value is fine.

I usually only use viewModelScope.launch when I’m calling suspend functions or need to switch threads. For simple updates, I just set the value directly — it’s safe and works as expected.

July 23, 2025 Score: 1 Rep: 22,192 Quality: Medium Completeness: 80%

You only need a coroutine to execute suspend functions or legacy code that isn't coroutine-aware. MutableStateFlow as a native Kotlin data structure is fully coroutine-aware. So only wrap things in a coroutine that the compiler would otherwise forbid you to call without.

The MutableStateFlow doesn't need a coroutine because updating the value is very fast (it's basically a simple assignment) and is therefore considered main-safe and can be executed synchronously.

Executing all collectors after the value changed, however, is done asynchronously and needs a coroutine. But that is handled by the collectors themselves (that's why collect() is a suspend function; and although collectAsState() itself isn't suspending, it uses a LaunchedEffect under the hood which creates a new coroutine).

Gratuitously wrapping suspicious code in a coroutine is not a good idea, though, because it incurs an unnecessary overhead setting up the coroutine and makes your code more complicated and harder to test.

For a MutableStateFlow, simply set the value without launching a coroutine. Just pay attention when you need the old value to calculate the new value, because some other thread running in parallel to yours may change that value at the same time. For example, this is prone to race conditions and may have unexpected results. Don't do this:

filter.value = filter.value + newFilter

Instead, use the update function:

filter.update { it + newFilter }

This also doesn't need a coroutine, but it is guaranteed to be executed atomically, without another thread interfering. For this to work the lambda may be executed multiple times, so make sure it runs fast and doesn't have any side effects. Only when the calculation done in the lambda is so complex that it wouldn't be main-safe anymore you should wrap the entire update in a coroutine. In that case you should also extract the complex logic into a dedicated function, make it suspendable and switch to another dispatcher in that function, as it is the general rule when performing operations that aren't main-safe. For example:

viewModelScope.launch {
filter.update { complexCalculation(it, newFilter) } }

With the complex logic extracted into a dedicated suspend function:

suspend fun complexCalculation(old: String, new: String): String = withContext(Dispatchers.Default) { old + new // imagine a long-running calculation here }