Skip to content

Beginner: Kotlin · Coroutines and Flow · topic 17 of 50 · 30 min

Flow and StateFlow

Explanation

A suspend function returns one value; a Flow emits many values over time, like live scores or database changes. A cold Flow built with flow { emit(...) } runs each time someone collects it. Operators like map and filter transform it. StateFlow is a hot flow that always holds a current value and is the standard way for a ViewModel to expose UI state; SharedFlow is for one-off events.

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.

kotlin
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")
}
emit sends values; collect receives them. collect is a suspend function.

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

kotlin
countdown(5)
    .filter { it % 2 == 1 }        // 5, 3, 1
    .map { "Tick $it" }
    .collect { println(it) }
Flow operators look like collection operators but work on values as they arrive.

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.

kotlin
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()
StateFlow always has a value (here starting at 0), so the UI can draw immediately.
TypeHot or coldHas a current value?Use for
FlowColdNoStreams from a data source, such as Room queries
StateFlowHotYes, always oneUI state in a ViewModel
SharedFlowHotNot requiredOne-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.

Think of it like this: A cold Flow is like a recorded lecture: each student who presses play starts from the beginning. A StateFlow is like the scoreboard at a stadium: it always shows the current score, and anyone who looks up sees it instantly, then sees each change.

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

One value → suspend Many values → Flow

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.

kotlin
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) }
}
Prints the initial message, then each update; the last is Rider is arriving.

Practice (0/3 done)

Task 1 · Question

What does this print? flow { emit(1); emit(2); emit(3) } .map { it * 10 } .filter { it > 10 } .collect { print("$it ") }

Task 2 · Question

Which type should a ViewModel expose for screen state?

Task 3 · Practice

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

Question 1

1.What does "cold" mean for a Flow?

Question 2

2.Which statement about StateFlow is true?

Question 3

3.Which is best for a one-off event like "navigate to the payment screen"?

Question 4

4.What can Room return to get updates whenever a table changes?

Read more (free)

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

Answer: A read-only StateFlow of UI state

Expose read-only state.

From ViewModel and MVVM

Which type should a ViewModel expose for screen state?Related

Answer: StateFlow

Read-only, always has a value.

From Interview questions: Kotlin

Which annotation processor setup does the current Room guide recommend?Related

Answer: KSP

KSP is faster; kapt is in maintenance mode.

From Room database

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.

From App architecture: layers and repositories

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

  1. This is the last topic of “Coroutines and Flow”. The module exam comes next.
  2. Module 4 exam: Coroutines and Flow · 13 questions · 25 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Android Studio and your first app. 50 topics left in the course before the final exam.

← Previous: Dispatchers and withContext