kotlin-flows
Kotlin Flows
This reference covers the Flow traps and semantic edge cases — Channel vs SharedFlow, callback bridging, retry/error handling, side effects in transforms, and KMP boundaries — not the basics of cold/hot streams, the operator table, or lifecycle-safe collection.
Channel vs SharedFlow — the semantics that bite
| Found | Action |
|---|---|
BroadcastChannel |
Migrate → SharedFlow (deprecated) |
ConflatedBroadcastChannel |
Migrate → StateFlow (deprecated) |
Channel for single-consumer fire-once events (nav, snackbars, one-shot effects) |
Keep — correct. Channel(BUFFERED).receiveAsFlow() |
Channel broadcast to multiple collectors |
Migrate → SharedFlow (see below) |
Channel as producer-consumer queue |
Keep — correct |
Channel.receiveAsFlow() is fan-out, NOT broadcast. With multiple collectors, each event reaches one collector (the framework picks which), not all of them. If every collector must see every event, you need SharedFlow. This is the trap that's easy to fall into when reaching for Channel to multicast.
For single-consumer one-shot events, Channel(BUFFERED).receiveAsFlow() is the default because send suspends until consumed (the event is queued, never dropped), whereas SharedFlow(replay = 0) drops the emission if no collector is active at the instant of emission. Expose the Flow, never the Channel, and collect with collect inside LaunchedEffect — not collectAsStateWithLifecycle, which retains the last event as state and re-fires it on every recomposition / config change.