โ† all topics02 / 05 ยท learn android

๐ŸŒŠ 02 ยท learn android

Kotlin Flow

Reactive streams on coroutines

A sequence of values over time, produced, transformed, and collected.

If a coroutine answers "run this work," a Flow answers "stream this data." A coroutine produces one result (or none, or throws); a Flow produces a sequence of values over time โ€” a live database query, a progress tick, a search box that emits as the user types. Flow is Kotlin's reactive-streams library, rebuilt on the coroutine machinery, so it inherits cancellation and structured concurrency for free.

Every Flow is three pieces wired together: a builder produces values, operators transform the stream, and a terminal operator collects it. Understand those three and every operator is just an instance of one of them.

The mental model

How it fits together

The pipeline โ€” producer โ†’ transformer โ†’ consumer

builder
โ†’
map
โ†’
filter
โ†’
debounce
โ†’
collect

nothing runs until the terminal operator subscribes

Cold vs hot

cold โ€” flow{ }

runs once per collector, from the start

hot โ€” StateFlow

emits regardless; late collectors see latest

instant search = debounce + filter + distinctUntilChanged + flatMapLatest

Key concepts

The ideas to internalise

Cold by default

A flow { } doesn't run when you build it โ€” only when something collects it, and once per collector. No background work unless a consumer is listening.

Builders

flow { emit(x) } for imperative logic, flowOf(1,2,3) for a fixed set, asFlow() to lift a collection, callbackFlow to bridge a callback API.

Operators

map, filter, debounce, zip, flatMapLatest, retry โ€” transform the stream between builder and collector. Operators are themselves flows that collect upstream and emit downstream.

Terminal operators

collect, toList, first, reduce โ€” what ends the stream. Nothing runs until one subscribes.

StateFlow / SharedFlow

The hot flows. StateFlow holds one value for UI state and conflates updates; SharedFlow broadcasts events. Late collectors see the latest, not history.

Backpressure

A slow collector makes the producer wait by default. Use buffer, conflate, or collectLatest to say explicitly how to handle a producer that outpaces the collector.

Example index

17 runnable examples

Each is a self-contained Activity + ViewModel pair you can open, read, and run.

  1. 01Single Network Callthe simplest Flow network call
  2. 02Series Network Callsdependent calls in sequence
  3. 03Parallel Network Callsindependent calls combined with zip
  4. 04Room DB OperationFlow with Room
  5. 05Long Running Taska background task as a Flow
  6. 06Two Long Running Taskstwo tasks in parallel
  7. 07Catch Error Handlingthe catch operator
  8. 08EmitAll Error Handlingrecovering with a fallback flow
  9. 09CompletiononCompletion
  10. 10Reducethe reduce operator
  11. 11Mapthe map operator
  12. 12Filterthe filter operator
  13. 13Search Featureinstant search: debounce + filter + distinctUntilChanged + flatMapLatest
  14. 14Retrythe retry operator
  15. 15RetryWhenconditional retry
  16. 16Retry with Exponential Backoffbacking off between retries
  17. 17Unit Testtesting a Flow-based ViewModel

The takeaway

The entire instant-search feature is four operators chained: debounce (wait for a pause) + filter (drop empties) + distinctUntilChanged (dedupe) + flatMapLatest (only the latest). Once you can read a chain like that, you can read any Flow.