Kotlin Flow Cheat Sheet
Building, transforming, and collecting cold asynchronous streams with Kotlin Flow, plus StateFlow/SharedFlow for hot state.
Building & Collecting a Flow
Flow is cold: the block only runs when collected, and runs fresh per collector.
import kotlinx.coroutines.flow.*import kotlinx.coroutines.*fun numbers(): Flow<Int> = flow { for (i in 1..5) { delay(100) // suspend inside the builder is fine emit(i) // push a value downstream }}suspend fun main() { numbers().collect { value -> println(value) // 1, 2, 3, 4, 5 -- printed as they're emitted }}// flowOf and asFlow for simple sourcesflowOf(1, 2, 3)listOf(1, 2, 3).asFlow()
Transforming Operators
Flow supports familiar sequence-like operators, all suspend-aware.
numbers() .map { it * it } .filter { it % 2 == 0 } .take(3) .onEach { println("saw $it") } .catch { e -> println("caught: $e") } // catches upstream exceptions .collect { println("result: $it") }// flatMapConcat / flatMapMerge / flatMapLatest for flows-of-flowssearchQuery .debounce(300) .flatMapLatest { query -> searchApi(query) } // cancels previous search on new input .collect { results -> render(results) }
Context Preservation & flowOn
Flow preserves the collector's context; flowOn changes upstream dispatch.
fun heavyWork(): Flow<Int> = flow { for (i in 1..5) { Thread.sleep(50) // pretend CPU-bound work emit(i) }}.flowOn(Dispatchers.Default) // upstream (the flow{} builder) runs on Defaultsuspend fun run() { withContext(Dispatchers.Main) { heavyWork().collect { value -> updateUI(value) // collect lambda runs on Main, as expected } }}
StateFlow & SharedFlow
Hot flows for UI state (StateFlow) and event broadcasting (SharedFlow).
class CounterViewModel : ViewModel() { private val _count = MutableStateFlow(0) // requires an initial value val count: StateFlow<Int> = _count.asStateFlow() fun increment() { _count.update { it + 1 } // atomic update }}class EventBus { private val _events = MutableSharedFlow<String>(replay = 0, extraBufferCapacity = 1) val events: SharedFlow<String> = _events.asSharedFlow() suspend fun publish(event: String) = _events.emit(event)}// Collecting StateFlow in Composeval count by viewModel.count.collectAsStateWithLifecycle()
StateFlow vs SharedFlow vs Flow
Choosing the right stream type.
- Flow- cold, no state, runs fresh per collector, for one-shot async sequences
- StateFlow- hot, always has a current value, conflates rapid updates, ideal for UI state
- SharedFlow- hot, configurable replay/buffer, ideal for one-off events (snackbars, navigation)
- channelFlow- builder for flows that need concurrent emit() from multiple coroutines
- stateIn / shareIn- convert a cold Flow into a hot StateFlow/SharedFlow scoped to a CoroutineScope
Don't put one-off events (like 'show a toast') in a StateFlow — because it always replays its latest value to new collectors, a screen rotation or re-subscription will re-fire the last event; use SharedFlow with replay=0 for events, and reserve StateFlow strictly for durable state.
Explore Topics
Professional Web Designing Services
- Responsive Websites
- E-commerce Solutions
- SEO Friendly Design
- Fast & Secure
- Support & Maintenance