StateFlow and SharedFlow

StateFlow, SharedFlow and Flow Operators

Screens need hot streams. A StateFlow<T> always holds a value, hands it to each new collector and skips duplicates, much like React 7,897 state; Compose 234 observes it (ViewModel and UDF). A SharedFlow<T> broadcasts events such as toasts, replaying none by default. stateIn turns a cold flow into state:

state.kt: a cart with StateFlow, SharedFlow and stateInKotlin
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
class CartModel(scope: CoroutineScope) {
  private val _count = MutableStateFlow(0)
  val count: StateFlow<Int> = _count.asStateFlow()               // read-only for the UI
  private val _events = MutableSharedFlow<String>()
  val events: SharedFlow<String> = _events.asSharedFlow()        // one-off messages
  val badge = count.map { "$it in cart" }.stateIn(scope, SharingStarted.Eagerly, "empty")
  suspend fun add(b: Book) { _count.update { it + 1 }; _events.emit("Added ${b.title}") }
}
fun main() = runBlocking {
  val cart = CartModel(this)
  launch { cart.count.collect { log("count = $it") } }
  launch { cart.events.collect { log("toast: $it") } }
  delay(10); cart.add(catalog[2]); cart.add(catalog[5]); delay(10)
  log("badge = ${cart.badge.value}")
  coroutineContext.cancelChildren()                              // collectors never complete
}
Output
   0 ms [main @coroutine#3] count = 0
 108 ms [main @coroutine#3] count = 1
 114 ms [main @coroutine#4] toast: Added Salt and Saffron
 117 ms [main @coroutine#3] count = 2
 125 ms [main @coroutine#4] toast: Added Gardens in Glass
 142 ms [main @coroutine#1] badge = 2 in cart

update { } is atomic. In a ViewModel, prefer SharingStarted.WhileSubscribed(5_000), which survives a rotation. Other everyday operators: debounce, combine, flatMapLatest, catch and retry.