Careeroza
StateFlow vs SharedFlow vs Channel
Comparison
| StateFlow | SharedFlow | Channel | LiveData | |
|---|---|---|---|---|
| Hot / cold | hot | hot | hot | hot |
| Has current value | yes | no (replay cache) | no | yes |
| Receivers | all collectors | all collectors | each value to one receiver | all observers |
| Duplicates | skipped | delivered | delivered | delivered |
| Platform | any (KMP) | any | any | Android only |
| Best for | UI state | events to many listeners | one-time events / work queues | older 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 withreceiveAsFlow(). 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()
}