skip to content

Flow

Flow is Kotlin's reactive stream type built on coroutines: cold producers, a rich operator set, hot StateFlow and SharedFlow for shared state and events, and backpressure through suspension. Any Android or streaming-backend interview goes here after coroutines.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

106 · 7 sections

What is a Kotlin Flow, and how does it relate to a regular list and to reactive streams like RxJava's Observable?

level: juniorimportance: must knowfreq 80%
basics
~10 s

A Flow is a stream of values produced over time, one after another. Unlike a list, the values can arrive asynchronously without blocking. It is Kotlin's coroutine-based answer to reactive streams.

open as a page

What does it mean that Flow is "cold," and what runs the flow's producer block? Show what happens if no one collects.

level: middleimportance: must knowfreq 75%
basics
~10 s

Cold means the flow does nothing until someone collects it. The producer code only runs when collect is called, and it runs fresh for each collector. No collector, no work.

open as a page

Explain how a Flow integrates with structured concurrency: in whose context does emission run, and how does cancellation propagate?

level: seniorimportance: should knowfreq 50%
basics
~10 s

A flow runs inside the coroutine that collects it. So it uses that coroutine's thread and lifecycle: if the collecting coroutine is cancelled, the flow stops too. Producer and consumer share one structured scope.

open as a page

How does Flow handle backpressure without an explicit request(n) mechanism like Reactive Streams?

level: seniorimportance: should knowfreq 60%
basics
~20 s

When the collector is slow, the producer's emit call simply suspends and waits. No values are dropped or buffered by default — the producer politely pauses until the collector is ready for the next value.

open as a page

Why did Kotlin model Flow as cold and suspend-based rather than adopting the Reactive Streams Publisher/Subscriber callback model? What trade-offs does that create, including interop?

level: principalimportance: nice to knowfreq 30%
basics
~20 s

Building Flow on coroutines lets async stream code read like normal sequential code, with built-in cancellation and backpressure via suspension. The trade-off is that the default Flow lacks multicasting and needs adapters to talk to RxJava/Reactor.

open as a page

What does flowOf(...) do, and how does it differ from a list of values?

level: juniorimportance: must knowfreq 60%
basics
~10 s

flowOf takes a fixed set of values and wraps them in a Flow, emitting each one in order when you collect it. A list just holds values; a Flow streams them on demand.

open as a page

What is the flow { } builder in Kotlin, and what does it mean that the flow it creates is 'cold'?

level: juniorimportance: must knowfreq 80%
basics
~10 s

flow { } makes a stream of values. Inside the block you call emit(value) to send each item. Nothing runs until someone collects it. A cold flow re-runs its code fresh for every collector.

open as a page

What does it mean that a Kotlin Flow is "cold", and what does that imply about when its code runs?

level: juniorimportance: must knowfreq 80%
basics
~10 s

A cold Flow does nothing on its own. The code inside it runs only when you collect it. Until someone collects, no values are produced and no work happens.

open as a page

In Kotlin Flow, what does 'context preservation' mean, and in which coroutine context does emit run by default?

level: juniorimportance: must knowfreq 70%
basics
~10 s

It means the code that produces values runs in the same place that collects them. By default, emit runs in the collector's coroutine, so the flow uses whatever context the collector started in.

open as a page

How does asFlow() work for Iterable and Sequence, and when would you use it over flowOf?

level: middleimportance: must knowfreq 50%
basics
~20 s

asFlow() is an extension that turns something you already have — a list, sequence, or range — into a Flow that emits each element. Use it when the values live in a collection; use flowOf for literal values you type out.

open as a page

What is the difference between zip and combine when joining two Flows in Kotlin?

level: juniorimportance: must knowfreq 70%
basics
~10 s

zip waits for one new item from each flow and pairs them in lockstep. combine fires whenever either flow emits, using the newest value from the other flow.

open as a page

What problem do flatMapConcat, flatMapMerge, and flatMapLatest solve in Kotlin Flow, and how do they differ at a high level?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Each emitted value can itself produce a flow. These operators flatten those inner flows into one stream. Concat runs them one after another, Merge runs them at the same time, Latest cancels the old inner flow when a new value comes.

open as a page

What is a terminal operator on a Kotlin Flow, and why must you call one for any code to run? Give two examples.

level: juniorimportance: must knowfreq 82%
basics
~10 s

A terminal operator is the function that actually starts a Flow running and consumes its values. Without one, nothing happens because Flows are lazy. Examples: collect and toList.

open as a page

What do the map and filter intermediate operators do on a Kotlin Flow, and why are they 'cold' and 'lazy'?

level: juniorimportance: must knowfreq 80%
basics
~10 s

map turns each emitted value into a new value; filter keeps only values matching a condition. They do nothing until a terminal operator (like collect) runs the flow, and each collection re-runs the work.

open as a page

Explain the ordering and concurrency guarantees of flatMapConcat versus flatMapMerge, including the concurrency argument and its default.

level: middleimportance: must knowfreq 60%
basics
~20 s

Concat processes inner flows one at a time, so output keeps the input order. Merge processes several at once, so faster inner flows can finish first and outputs interleave. Merge lets you cap how many run together; the default is 16.

open as a page

What is a SharedFlow in Kotlin coroutines, and how does it differ from a regular (cold) Flow?

level: juniorimportance: must knowfreq 70%
basics
~10 s

SharedFlow is a hot stream that broadcasts the same values to all collectors at the same time. A regular Flow is cold and re-runs its code separately for each collector.

open as a page

What do the shareIn and stateIn operators do to a cold Flow, and what is the difference between their results?

level: juniorimportance: must knowfreq 70%
basics
~10 s

Both turn a normal Flow that restarts for each collector into a shared one that runs once for everybody. shareIn gives a SharedFlow; stateIn gives a StateFlow that always has a current value.

open as a page

What is StateFlow in Kotlin coroutines, and how does it differ from a plain (cold) Flow?

level: juniorimportance: must knowfreq 80%
basics
~10 s

StateFlow is a flow that always holds one current value you can read at any time through .value. A plain Flow holds nothing on its own and only produces values when someone collects it.

open as a page

Explain the three MutableSharedFlow constructor parameters: replay, extraBufferCapacity, and onBufferOverflow. How do they interact?

level: middleimportance: must knowfreq 65%
basics
~10 s

replay sets how many recent values new collectors get. extraBufferCapacity adds room for emitters when collectors are slow. onBufferOverflow decides what happens when both are full: suspend, drop oldest, or drop newest.

open as a page

Compare SharingStarted.Eagerly, Lazily, and WhileSubscribed. How does each control when the shared upstream starts and stops?

level: middleimportance: must knowfreq 65%
basics
~20 s

Eagerly starts the upstream right away and never stops. Lazily starts on the first collector and never stops. WhileSubscribed starts on the first collector and stops when the last one leaves, optionally after a timeout.

open as a page

What does the buffer() operator do to a Kotlin Flow, and why would you add it to a pipeline?

level: juniorimportance: must knowfreq 70%
basics
~20 s

buffer() lets the part producing values and the part collecting them run at the same time instead of taking turns. Producers can race ahead into a small queue, so a slow collector no longer slows the producer.

open as a page

What does Flow's collectLatest do, and how is it different from a plain collect?

level: juniorimportance: must knowfreq 70%
basics
~20 s

collectLatest runs your handling code for each value, but if a new value arrives before the previous one finishes, it stops the previous work and starts on the new value. Plain collect always finishes every value first.

open as a page

What does the conflate() operator do to a Kotlin Flow, and when would you reach for it?

level: juniorimportance: must knowfreq 55%
basics
~20 s

conflate() lets a fast producer skip ahead: if the collector is busy, in-between values are dropped and it only gets the newest one. Good when you only care about the latest value, like a live counter.

open as a page

Explain the three BufferOverflow strategies for buffer() and what each does when the buffer is full.

level: middleimportance: must knowfreq 60%
basics
~20 s

When the buffer fills up: SUSPEND pauses the producer until there is room (nothing lost); DROP_OLDEST throws away the oldest queued item to make space for the new one; DROP_LATEST throws away the new item and keeps what is already queued.

open as a page

Why might collectLatest fail to cancel an in-flight block, and how do you make CPU-bound work in the block actually cancellable?

level: middleimportance: must knowfreq 55%
basics
~20 s

Cancellation only happens when the running code reaches a pause point. If your block does heavy non-stop computation, there's no pause point, so the old block keeps running. Add checks like ensureActive() or yield() so it can stop.

open as a page

What is callbackFlow and when would you reach for it instead of the plain flow { } builder?

level: juniorimportance: must knowfreq 70%
basics
~10 s

callbackFlow turns an old-style callback or listener API into a Flow. You use it when values arrive through a callback you register, not from suspending code you call directly.

open as a page

What is channelFlow { } and how does it differ from the plain flow { } builder?

level: juniorimportance: must knowfreq 60%
basics
~10 s

channelFlow builds a cold Flow but lets you emit values from several coroutines at once using send(). The plain flow builder only lets you emit from one place, sequentially.

open as a page

What is the core difference between a Channel and a Flow in Kotlin coroutines, and what does it mean to call a Channel 'hot' and a Flow 'cold'?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A Channel is like a queue that passes items between coroutines; each item is taken by one receiver. A Flow is a recipe that re-runs from the start for every collector. Channel is hot (always live); Flow is cold (starts when collected).

open as a page

What is the role of awaitClose { } in callbackFlow, and what happens if you omit it?

level: middleimportance: must knowfreq 65%
basics
~10 s

awaitClose keeps the flow alive until it is cancelled or closed, then runs cleanup like unregistering the listener. If you leave it out, the flow ends immediately and Kotlin throws an error.

open as a page

Why does emitting from a launched coroutine inside flow { } throw an exception, and how does channelFlow solve it?

level: middleimportance: must knowfreq 50%
basics
~20 s

flow { } requires that all emit() calls happen in the same coroutine, so emitting from a new coroutine breaks that rule and throws. channelFlow uses a channel and a thread-safe send(), so any coroutine can produce values.

open as a page

What does the `catch { }` operator do in a Kotlin Flow, and which exceptions can it handle?

level: juniorimportance: must knowfreq 70%
basics
~10 s

catch runs when something fails earlier in the flow (upstream). It only catches errors from the parts above it, lets you log them, and can send a backup value instead of crashing.

open as a page

What do the Flow operators onStart and onCompletion do, and when does each block run?

level: juniorimportance: must knowfreq 70%
basics
~10 s

onStart runs once right before a flow starts producing values. onCompletion runs once after the flow finishes, whether it ended normally, was cancelled, or threw an error.

open as a page

What does the Flow operator retry(count) do, and where does it sit in a Flow pipeline?

level: juniorimportance: must knowfreq 62%
basics
~10 s

retry re-subscribes to the flow when it fails with an error, trying again up to the given number of times before letting the error through.

open as a page

Explain the principle of "exception transparency" in Kotlin Flow. Why is wrapping `emit` in a try/catch a violation?

level: middleimportance: must knowfreq 55%
basics
~20 s

Exception transparency means a flow must let downstream errors flow back out, not swallow them. So you should never put a try/catch around emit, because that would accidentally hide errors that really belong to the collector.

open as a page

How does retryWhen { cause, attempt -> } work, and what do its parameters mean?

level: middleimportance: must knowfreq 55%
basics
~10 s

retryWhen runs a function each time the flow fails. You get the error and how many times it has already retried, and you return true to try again or false to give up.

open as a page