Beginner: Kotlin · Coroutines and Flow · topic 17 of 50 · 30 min
Flow and StateFlow
Explanation
One value versus many
Some data arrives once: your account balance right now. Other data keeps changing: a cricket score, a delivery rider's location, the list of rows in a database table that the user keeps editing. A Flow is a stream of values delivered one after another over time, built on coroutines.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun countdown(from: Int): Flow<Int> = flow {
for (i in from downTo 1) {
emit(i) // send a value to the collector
delay(1000)
}
}
fun main() = runBlocking {
countdown(3).collect { println("OTP expires in $it s") }
println("OTP expired")
}Cold flows
A flow built with flow { } is cold: its code does nothing until someone calls collect, and it runs again from the start for each collector, like a recorded video that starts playing when you press play. Room can return Flow<List<T>> from a query, which emits a new list every time the table changes.
Operators
countdown(5)
.filter { it % 2 == 1 } // 5, 3, 1
.map { "Tick $it" }
.collect { println(it) }StateFlow: the current state
A screen needs to know what to show right now, and to update when that changes. StateFlow is a hot flow that always holds exactly one current value. New collectors get the latest value immediately. MutableStateFlow lets the owner change the value; the owner exposes a read-only StateFlow to everyone else, the same private-mutable, public-read-only pattern you saw with lists.
import kotlinx.coroutines.flow.*
class CartState {
private val _count = MutableStateFlow(0)
val count: StateFlow<Int> = _count.asStateFlow()
fun add() { _count.value += 1 }
}
// In Compose (module 6/7): val count by viewModel.count.collectAsStateWithLifecycle()| Type | Hot or cold | Has a current value? | Use for |
|---|---|---|---|
| Flow | Cold | No | Streams from a data source, such as Room queries |
| StateFlow | Hot | Yes, always one | UI state in a ViewModel |
| SharedFlow | Hot | Not required | One-off events: show a message, navigate |
Hot means it exists and holds or emits values whether or not anyone is collecting. A ViewModel can also turn a cold Flow into a StateFlow with stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), initialValue), which keeps one shared upstream while the screen is visible.
Collecting safely on Android
Collecting a flow when the screen is not visible wastes work and can crash when the UI is gone. In Compose, use collectAsStateWithLifecycle(), which collects only while the screen is at least started. You will use it in module 7.
Common mistakes
- Expecting a flow { } block to run without collect. Cold flows are lazy.
- Exposing MutableStateFlow publicly. Expose StateFlow and change state only inside the owner.
- Using StateFlow for one-time events like "show a toast": a new collector would see it again after rotation. Model it as state that is cleared, or use a SharedFlow/Channel carefully.
- Collecting flows in Compose with collectAsState without lifecycle awareness for long-lived screens. Prefer collectAsStateWithLifecycle().
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Flow and StateFlow · 1/7
A suspend function returns one value. A Flow emits many values over time.
Prefer a video by a teacher? Find free videos on “kotlin flow stateflow sharedflow android tutorial” on YouTube ↗
In real life: Live delivery tracking
A grocery delivery app shows the rider's distance from your home updating every few seconds. The repository exposes a Flow<Int> of distances from the server. The ViewModel converts it into a StateFlow<String> with messages like "Rider is 1.2 km away", starting with "Locating rider...". The screen collects it with lifecycle awareness, so tracking pauses when the user switches apps.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun riderDistance(): Flow<Int> = flow {
for (m in listOf(1800, 1200, 600, 150)) { emit(m); delay(500) }
}
fun main() = runBlocking {
val state = MutableStateFlow("Locating rider...")
println(state.value)
riderDistance()
.map { if (it < 200) "Rider is arriving" else "Rider is $it m away" }
.collect { state.value = it; println(state.value) }
}Practice (0/3 done)
What does this print? flow { emit(1); emit(2); emit(3) } .map { it * 10 } .filter { it > 10 } .collect { print("$it ") }
Which type should a ViewModel expose for screen state?
Write a flow that emits stock prices every 300 ms for 6 ticks (use a list of numbers). Collect it, keep a MutableStateFlow<Int> with the highest price seen, and print "New high: X" only when it changes. Then explain in two lines the difference between the cold flow and the StateFlow you used.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- launch, async and structured concurrencyCoroutines and Flow · same module
- ViewModel and MVVMNavigation and app architecture
- Interview questions: KotlinAndroid career path
- Room databaseSaving data and calling APIs
- App architecture: layers and repositoriesNavigation and app architecture
- Interview questions: Android and ComposeAndroid career path
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does "cold" mean for a Flow?
Answer: Its code runs only when collected, separately for each collector
Cold flows start on collect.
Which statement about StateFlow is true?
Answer: It always holds a current value
StateFlow requires an initial value and always has one.
Which is best for a one-off event like "navigate to the payment screen"?
Answer: SharedFlow (or a state that is cleared after handling)
Events should not be replayed like state.
What can Room return to get updates whenever a table changes?
Answer: A Flow<List<T>>
Room emits a new list on every change.
What does the ViewModel expose to the UI?Related
Which type should a ViewModel expose for screen state?Related
Which annotation processor setup does the current Room guide recommend?Related
In unidirectional data flow, which way do events go?Related
Answer: Up from the UI to the ViewModel and below
Events go up, state comes down.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Coroutines and Flow”. The module exam comes next.
- Module 4 exam: Coroutines and Flow · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
- Then the next module, Android Studio and your first app. 50 topics left in the course before the final exam.