Flow Basics
Creating a Flow
flow { } builds a cold stream that emits values one at a time using emit(); nothing inside runs until the flow is collected.
Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.
Example: Creating a Flow
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun numberFlow(): Flow<Int> = flow {
for (i in 1..3) {
delay(20)
emit(i)
}
}
fun main() = runBlocking {
numberFlow().collect { value ->
println("Collected: $value")
}
}
Login to try C/C++/Java/PHP code in the editor
Flows Are Cold
A Flow does not start producing values until .collect { } (or another terminal operator) is actually called on it, meaning the same flow can be collected multiple times, running its logic fresh each time.
Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.
Example: Flows Are Cold
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun greetingFlow(): Flow<String> = flow {
println("Flow started")
emit("Hello")
}
fun main() = runBlocking {
println("Before collecting")
val flow = greetingFlow()
println("Flow created, but not started yet")
flow.collect { println(it) }
}
Login to try C/C++/Java/PHP code in the editor
Transforming a Flow
Operators like .map { } and .filter { } work on a Flow much like they do on a collection, building up a pipeline that runs when the flow is finally collected.
Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.
Example: Transforming a Flow
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
(1..5).asFlow()
.filter { it % 2 == 0 }
.map { it * 10 }
.collect { println("Value: $it") }
}
Login to try C/C++/Java/PHP code in the editor
Flow vs a Single Suspend Result
While a plain suspend fun returns exactly one value, a Flow is built for representing zero, one, or many values arriving over time, making it a better fit for streams of ongoing data.
Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.
Example: Flow vs a Single Suspend Result
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val total = (1..5).asFlow().fold(0) { acc, value -> acc + value }
println("Total from flow: $total")
}
Login to try C/C++/Java/PHP code in the editor
- Forgetting that a
Flowis cold -- its code doesn't run at all until something actually collects it with.collect { }. - Blocking inside a
Flow's collector instead of using suspend-friendly operations, which can defeat the purpose of using a Flow. - Confusing
Flow(built for potentially many asynchronous values over time) with a singlesuspend funreturning one value, or withSequence(synchronous, not suspend-aware).
flow { }builds a cold asynchronous stream of values, emitted one at a time withemit().- A
Flowdoes nothing until.collect { }is called on it -- it is lazily executed, similar in spirit to aSequence. - Flow operators like
.map { }and.filter { }transform the stream, similar to collection operations but suspend-aware. - Flows are provided by
kotlinx.coroutinesand are commonly used for streams of data that arrive over time.
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: