skip to content

Hot Flows

Hot flows exist independently of collectors and broadcast to all of them, which is what you need for shared state and events. StateFlow versus SharedFlow, and how to make a cold flow hot, is a standard question set.

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

explore

questions

15

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

level: juniorimportance: must knowfreq 70%

answer

  1. Hot + multicast = SharedFlow
  2. Cold flow { } restarts per collector
  3. MutableSharedFlow = producer side (emit/tryEmit)
  4. asSharedFlow() to expose read-only
  5. No initial value unlike StateFlow

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.

solid answer

~40 s

A SharedFlow is a hot Flow: it stays active independently of collectors and broadcasts each emitted value to all current subscribers simultaneously. A cold Flow (built with flow { }) is inert until collected and restarts its producer block for every collector, so each collector gets its own independent run. SharedFlow is created via MutableSharedFlow(...), you push values with emit (suspending) or tryEmit (non-suspending), and read-only consumers see it typed as SharedFlow. It has no current value by default and no initial value, which makes it a natural event bus for one-off events (navigation, toasts, signals). It is the broadcast superclass of StateFlow.

go deeper

for a junior

Can state hot vs cold and that SharedFlow broadcasts to all collectors.

for a middle

Explains the read-only/mutable split and asSharedFlow() encapsulation, and the no-initial-value contrast with StateFlow.

for a senior

Frames SharedFlow as an event bus, mentions replay/buffer knobs and never-completes semantics, and knows it's StateFlow's superclass.

for a principal

Reasons about when broadcast-event semantics fit the architecture vs StateFlow/Channel and the lifecycle/back-pressure implications of a never-completing hot stream.

## Cold vs Hot A **cold** Flow (created with `flow { ... }`) is just a recipe: nothing runs until you call `collect`, and the producer block runs **separately for each collector**. Two collectors each get their own independent execution. A **hot** Flow exists and runs independently of whether anyone is collecting. `SharedFlow` is the canonical hot, multicast (broadcast) Flow: a single emitter feeds **all** active collectors the same values at the same time. ## SharedFlow vs MutableSharedFlow - `SharedFlow<T>` is the **read-only** interface a consumer collects from. - `MutableSharedFlow<T>` adds the producer side: `emit` and `tryEmit`. - You typically keep a private `MutableSharedFlow` and expose it as a public `SharedFlow` via `asSharedFlow()` so outsiders can't emit. ```kotlin private val _events = MutableSharedFlow<UiEvent>() val events: SharedFlow<UiEvent> = _events.asSharedFlow() suspend fun signal() { _events.emit(UiEvent.Refresh) } ``` ## Key properties - **No initial value / no current value**: unlike `StateFlow`, a fresh `SharedFlow` has nothing to show a new subscriber unless `replay > 0`. - **Multicast**: every collector observes the same emissions; the flow never terminates on its own. - **Buffering**: configurable via `replay`, `extraBufferCapacity`, and `onBufferOverflow`. ## When to use it Use `SharedFlow` for **events** that should fire once and be delivered to whoever is listening: navigation commands, snackbars, analytics signals. Use `StateFlow` for **state** (a single current value). For turning a cold Flow hot, prefer the `shareIn` operator (a sibling topic) rather than manual emission.

  • Why expose MutableSharedFlow as SharedFlow with asSharedFlow()?
    To enforce encapsulation: external code can collect but cannot emit, keeping the producer the single source of emissions.
  • Does a SharedFlow ever complete?
    No, by design it never completes on its own; collectors stay subscribed until their coroutine is cancelled.

A cold Flow is a vending machine each person operates themselves; a SharedFlow is a live radio broadcast everyone tuned in hears at once.

saying these in an interview costs you the question

  • Claiming SharedFlow re-runs a producer block per collector (that's cold flow)
  • Saying SharedFlow always has a current value (it doesn't unless replay>0)
  • Confusing SharedFlow with Channel and assuming exactly-once-per-consumer delivery
  • Thinking collect() on a SharedFlow eventually returns normally

context

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

Explain conflation and equality-based deduplication in StateFlow. What happens if you set .value to a value equal to the current one?

level: middleimportance: must knowfreq 70%

basics

~10 s

StateFlow only keeps the latest value (conflation) and ignores a new value that equals the current one, so collectors don't get notified of 'changes' to the same value.

open as a page

When would you choose SharedFlow over StateFlow? Give concrete scenarios and the trade-offs.

level: seniorimportance: must knowfreq 60%

basics

~10 s

Use StateFlow for a current value (state) that the UI reads. Use SharedFlow for one-off events like navigation or toasts, where there's no meaningful current value and you don't want conflation or re-delivery.

open as a page

What is the difference between emit and tryEmit on a MutableSharedFlow, and when can tryEmit return false?

level: middleimportance: should knowfreq 55%

basics

~10 s

emit is a suspend function that waits if the buffer is full. tryEmit is non-suspending and returns true/false: it returns false when it would have to suspend to deliver the value.

open as a page

Explain the replay parameter of shareIn and the initialValue of stateIn, and how a new collector experiences each.

level: middleimportance: should knowfreq 50%

basics

~10 s

shareIn's replay says how many recent values a brand-new collector instantly receives. stateIn always replays exactly the one latest value, and its initialValue is what collectors see before the upstream has emitted anything.

open as a page

When updating a MutableStateFlow, why prefer update { } / compareAndSet over reading and reassigning .value? Show a correct pattern.

level: middleimportance: should knowfreq 55%

basics

~10 s

Reading .value then writing it back can lose updates when two threads do it at once. update { } applies your change atomically, so concurrent updates don't clobber each other.

open as a page

With SharingStarted.WhileSubscribed, what happens to the upstream coroutine across subscriber gaps, and what are the resource and correctness implications versus Eagerly?

level: seniorimportance: should knowfreq 45%

basics

~10 s

WhileSubscribed cancels the upstream when nobody is listening (after a timeout) and restarts it when a collector returns, so expensive work pauses and resumes. Eagerly keeps it running forever, wasting resources but never restarting.

open as a page

When a new collector subscribes to a StateFlow, what does it receive, and how does replay-of-1 interact with conflation for a slow collector?

level: seniorimportance: should knowfreq 50%

basics

~10 s

A new collector immediately gets the current value, then future changes. If the collector is slow, it can skip intermediate values and only sees the latest one each time it's ready.

open as a page

You are designing a ViewModel exposing UI state and one-off events. How do you decide between stateIn, shareIn, and a MutableStateFlow/MutableSharedFlow, and what scope and SharingStarted do you pick?

level: principalimportance: should knowfreq 40%

basics

~10 s

Use stateIn for continuous UI state derived from a flow, shareIn for shared event streams, and a manual MutableStateFlow/SharedFlow when you push values yourself. Host them in viewModelScope and usually start with WhileSubscribed(5000).

open as a page

What does MutableSharedFlow.subscriptionCount give you, and what is resetReplayCache used for? Sketch a use case.

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

subscriptionCount is a StateFlow of how many collectors are active right now. resetReplayCache empties the cache of recent values so new subscribers don't get old ones. You use subscriptionCount to start/stop upstream work on demand.

open as a page

Design-wise, when is StateFlow the right state-holder and when is it a poor fit? Address null handling, the always-present value, and event delivery.

level: principalimportance: nice to knowfreq 35%

basics

~10 s

Use StateFlow when there's always a meaningful current state to read. It's a poor fit for one-time events, because equal values are skipped and intermediate values can be dropped.

open as a page