StateFlow vs SharedFlow vs Channel

Comparison

StateFlowSharedFlowChannelLiveData
Hot / coldhothothothot
Has current valueyesno (replay cache)noyes
Receiversall collectorsall collectorseach value to one receiverall observers
Duplicatesskippeddelivereddelivereddelivered
Platformany (KMP)anyanyAndroid only
Best forUI stateevents to many listenersone-time events / work queuesolder Java code

Choosing

  • Screen state (loading, list, error): StateFlow<UiState>.
  • One-time UI events (navigate, show snackbar) that must not be lost while the screen is in the background: Channel(Channel.BUFFERED) exposed with receiveAsFlow(). Many teams instead store the event in the state and clear it after it is shown.
  • Broadcast to several parts of the app (logout happened, cart changed): SharedFlow.

Exercise: Create a MutableStateFlow holding a data class Filter(val veg: Boolean, val maxPrice: Int). Combine it with a flow of dishes and print the filtered list each time the filter changes.

Show answer
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

data class Dish(val name: String, val veg: Boolean, val price: Int)
data class Filter(val veg: Boolean = false, val maxPrice: Int = 1000)

fun main() = runBlocking {
    val dishes = MutableStateFlow(listOf(Dish("Paneer Tikka", true, 280), Dish("Chicken 65", false, 320), Dish("Dosa", true, 90)))
    val filter = MutableStateFlow(Filter())
    val job = launch {
        combine(dishes, filter) { list, f -> list.filter { (!f.veg || it.veg) && it.price <= f.maxPrice }.map { it.name } }
            .collect { println(it) }
    }
    delay(10); filter.update { it.copy(veg = true) }
    delay(10); filter.update { it.copy(maxPrice = 100) }
    delay(10); job.cancel()
}