An introduction to Flow
A suspend function returns one value, eventually. But a lot of data arrives as a stream — a
sequence of values over time: search results as the user types, location updates, messages in a chat,
rows from a database that change. Flow is Kotlin's tool for exactly this — an asynchronous stream
of values — and it is the last piece of the coroutines module and a cornerstone of modern Android.
The idea: many values over time
Compare the shapes:
- A
suspend fungives you one value, later:suspend fun fetchUser(): User. - A
Flowgives you many values, over time:fun temperatureUpdates(): Flow<Int>.
Think of a Flow as the asynchronous cousin of a Sequence (from the collections module): lazy, and
producing values one at a time — but where a sequence is synchronous and pulls values as fast as it
can, a Flow is asynchronous and can delay or wait between values, emitting them as they become
ready.
Building and collecting a Flow
You build a Flow with the flow { } builder, emitting values with emit, and you consume it by
collecting it:
fun countdown(): Flow<Int> = flow {
for (i in 3 downTo 1) {
delay(500) // wait between values — a Flow can be slow and asynchronous
emit(i) // push a value to whoever is collecting
}
emit(0)
}
fun main() = runBlocking {
countdown().collect { value ->
println(value) // runs for each emitted value, as it arrives
}
}
3 (after 0.5s)
2 (after 1.0s)
1 (after 1.5s)
0
Read the two halves. The flow { } builder is the producer: it emits values, and it can suspend
(delay) between them. collect { } is the consumer: its lambda runs once for each value as it
arrives. Between them flows a stream of values over time.
Two crucial properties:
- Flows are cold. Nothing happens until you
collect. Theflow { }block does not run when you create the Flow — only when a collector subscribes. Each collection starts the producer afresh. (This is like aSequence: describing the stream is separate from running it.) collectis a suspend function. Collecting waits for and processes each value, so it must happen in a coroutine — which is why the example is insiderunBlocking.
Transforming a Flow — the operators you already know
Here is the lovely part: a Flow supports the same operations as a collection — map, filter,
and friends — because they are the same idea applied to a stream:
fun main() = runBlocking {
countdown()
.filter { it > 0 } // drop the zero
.map { "T-minus $it" } // transform each value
.collect { println(it) }
}
T-minus 3
T-minus 2
T-minus 1
filter and map on a Flow do exactly what they do on a list — but lazily and asynchronously,
transforming each value as it flows through, without collecting the whole stream first. Everything
you learned about map/filter in the collections module transfers directly. This is why the course
spent so long on those operations: they are not just for lists, they are the vocabulary of data
transformation everywhere in Kotlin, streams included.
Where Flows are used
Flows model any data that arrives over time, which is a huge category on Android:
- A database that changes — Room (Android's database library) exposes query results as a
Flow, so when the underlying data changes, your Flow emits the new results and the UI updates automatically. No manual refresh. - User input over time — a search box emitting each query as the user types, so results update
live (usually with
debounceto wait for a pause in typing). - Location, sensors, network status — anything that produces a series of readings.
- UI state — a
StateFlow(a special Flow that always has a current value) is the modern, standard way aViewModelexposes screen state to the UI, replacing older patterns.
You will use StateFlow constantly in the Android course. The foundation to take now is the shape:
a producer emits values over time, and a collector reacts to each — with all the familiar
transformation operators in between.
Flow versus suspend function — choosing
The decision is simply about how many values:
- One value, once: a
suspend fun. "Fetch this user", "save this order". - Many values, over time: a
Flow. "Emit search results as they change", "stream temperature readings", "expose the current UI state".
If you find yourself calling a suspend function in a loop to poll for changes, that is usually a sign the data should be a Flow instead — let it push values to you as they change, rather than you pulling repeatedly.
Check your work
What a Flow represents. An asynchronous stream of many values over time — the multi-value cousin
of a suspend fun.
The synchronous analogue of a Flow. A Sequence — lazy, one value at a time — but Flow is
asynchronous and can wait between values.
How you build and consume a Flow. Build with flow { } and emit; consume with collect { }.
What "cold" means. Nothing runs until you collect; each collection starts the producer afresh.
Why collect must be in a coroutine. It is a suspend function — it waits for and processes each
value.
What operators a Flow supports. The same as collections — map, filter, and friends — applied
lazily and asynchronously to the stream.
Four things modelled as Flows on Android. A changing database (Room), live user input, sensors/
location/network status, and UI state (StateFlow).
When to use a Flow versus a suspend function. Many values over time → Flow; one value once → suspend function.
Practice
- Write a
countdown(): Flow<Int>that emits 3, 2, 1, 0 with adelaybetween each, andcollectit, printing each value as it arrives. - Confirm the Flow is cold: create it but do not collect, and observe that nothing prints.
- Add
filter { it > 0 }andmap { "T-minus $it" }before collecting. Confirm the output transforms just as it would for a list. - Emit values with no delay and collect them — note it behaves like a lazy sequence.
- Write a Flow that emits five random numbers with a delay, and collect only those above a
threshold with
filter. - Explain, for three kinds of data (a fetched user, a search box's queries, a temperature sensor), whether each is a suspend function or a Flow, and why.
- Take an imagined "poll every second for changes" loop of suspend calls and describe how a Flow would replace it.
Official documentation
- Kotlin — Asynchronous Flow — Building, collecting, cold flows, and operators.
- Kotlin — Flow operators —
map,filterand the transformation operators. - Android — Kotlin Flows on Android — Room,
StateFlow, and real usage. - Android — StateFlow and SharedFlow — The UI-state Flows you will use most.
Next module — Testing and Tooling: Gradle, tests, and structuring a real project.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship