← All courses

Flow: Reactive Streams

🗓 May 31, 2026 ⏱ 2 min read

From one value to many

A suspend function returns a single value once. But often you need a stream of values over time: live search results, a database that updates, a timer. Flow is Kotlin’s tool for this — an asynchronous sequence you can transform and collect.

Creating a flow

fun countdown(from: Int): Flow<Int> = flow {
    for (i in from downTo 1) {
        emit(i)          // send a value
        delay(1000)
    }
}

Collecting it

lifecycleScope.launch {
    countdown(3).collect { value ->
        println(value)   // 3, 2, 1 (one per second)
    }
}

A flow is “cold”: nothing runs until you collect it.

Transforming flows

Flows support the same readable operators as collections:

searchQueries
    .debounce(300)                  // wait for the user to stop typing
    .filter { it.length > 2 }
    .map { query -> api.search(query) }
    .collect { results -> show(results) }

StateFlow: holding the latest value

For UI state you usually want to always have a current value. StateFlow is a “hot” flow that holds the latest value — ideal in a ViewModel.

class SearchViewModel : ViewModel() {
    private val _state = MutableStateFlow<UiState>(UiState.Idle)
    val state: StateFlow<UiState> = _state   // read-only to the UI

    fun search(q: String) {
        viewModelScope.launch {
            _state.value = UiState.Loading
            _state.value = UiState.Success(api.search(q))
        }
    }
}

Flow vs StateFlow

  • Flow — cold, starts on collect, good for one-shot streams (a network response stream, a DB query).
  • StateFlow — hot, always has a current value, perfect for observable UI state.

Common mistakes

  • Expecting a cold Flow to run without collecting it.
  • Exposing a MutableStateFlow publicly — expose the read-only StateFlow.
  • Collecting a flow without a lifecycle-aware scope, wasting work when the screen is hidden.
Summary: Use Flow for streams of values over time and StateFlow to hold and expose the latest UI state. Transform them with the same operators you use on collections.