Careeroza
What is Flow
Many Values Over Time
A suspend function returns one value. A Flow<T> emits many values one after another, asynchronously: live cricket scores, search results while typing, rows from a database that keep changing.
| Returns | Example | |
|---|---|---|
| normal function | one value, now | fun total(): Int |
| suspend function | one value, later | suspend fun fetchUser(): User |
Sequence<T> | many values, now (blocking) | generateSequence |
Flow<T> | many values, later (suspending) | fun prices(): Flow<Int> |
Flows Are Cold
Example
A flow is only a recipe. The code inside flow { } runs each time someone calls collect, and not before.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun scores(): Flow<Int> = flow {
println(" (flow started)")
for (run in listOf(4, 6, 1)) {
delay(100)
emit(run)
}
}
fun main() = runBlocking {
val flow = scores()
println("Flow created, nothing ran yet")
println("Collector 1:")
flow.collect { println(" got $it") }
println("Collector 2:")
flow.collect { println(" got $it") }
}Output
Flow created, nothing ran yet Collector 1: (flow started) got 4 got 6 got 1 Collector 2: (flow started) got 4 got 6 got 1
Flow Builders
Example
Different ways to create a flow.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
println(flowOf("UPI", "Card", "Wallet").toList())
println(listOf(10, 20, 30).asFlow().toList())
println((1..5).asFlow().toList())
println(emptyFlow<String>().toList())
val countdown = flow {
for (i in 3 downTo 1) emit("T-$i")
emit("Liftoff")
}
println(countdown.toList())
}Output
[UPI, Card, Wallet] [10, 20, 30] [1, 2, 3, 4, 5] [] [T-3, T-2, T-1, Liftoff]
For callback-based APIs (location updates, Firebase listeners, sensors) use callbackFlow:
fun FusedLocationProviderClient.locationFlow(request: LocationRequest): Flow<Location> = callbackFlow {
val callback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
result.lastLocation?.let { trySend(it) }
}
}
requestLocationUpdates(request, callback, Looper.getMainLooper())
awaitClose { removeLocationUpdates(callback) } // runs when the collector stops
}Flow Rules
emitmust be called from the same coroutine asflow { }. Do notlaunchinsideflow { }and emit from there; usechannelFlowfor that.- Do not use
withContextinsideflow { }to change the thread of emissions; useflowOn. - Collection is sequential: the next value is produced only after the collector finished the previous one (unless you add a buffer).