skip to content

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%

answer

  1. Cold restarts per collector; hot shared once
  2. shareIn -> SharedFlow (replay configurable)
  3. stateIn -> StateFlow (always has .value)
  4. Both need scope + SharingStarted
  5. stateIn needs initialValue

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.

solid answer

~40 s

A cold Flow re-executes its producer block for every collector. shareIn and stateIn convert it into a hot flow whose single upstream is shared by all collectors. shareIn(scope, started, replay) returns a SharedFlow with a configurable replay cache. stateIn(scope, started, initialValue) returns a StateFlow, which is a SharedFlow specialization that always holds exactly one latest value (.value), conflates updates, and emits that value immediately to new collectors. Both need a CoroutineScope to host the shared upstream coroutine and a SharingStarted policy controlling when the upstream starts and stops. stateIn requires an initialValue (unless you use the suspending stateIn overload that waits for the first emission); shareIn does not.

code

kotlin · 3 lines
kotlin
val shared: SharedFlow<Int> = upstream.shareIn(scope, SharingStarted.Lazily, replay = 1)
val state: StateFlow<Int> = upstream.stateIn(scope, SharingStarted.Eagerly, initialValue = 0)
println(state.value) // StateFlow always has a current value

go deeper

for a junior

Knows shareIn yields SharedFlow and stateIn yields StateFlow, and that both make a cold flow shared.

for a middle

Explains cold-vs-hot precisely and why stateIn needs an initialValue while shareIn does not.

for a senior

Connects sharing to deduplicating an expensive upstream (one subscription for N collectors) and notes conflation/distinct semantics of StateFlow.

for a principal

Frames the choice as an API/lifecycle contract decision and reasons about scope ownership and resource lifetime implications.

## Cold vs hot A **cold** `Flow` is lazy: the lambda you pass to `flow { ... }` (or operators like `map`) re-runs *from scratch* for **each** collector. Two collectors mean two independent executions. A **hot** flow has a single producer running independently of collectors, and all collectors observe the *same* stream. `shareIn` and `stateIn` are the operators that **convert cold to hot** inside a `CoroutineScope`. ## shareIn ```kotlin fun <T> Flow<T>.shareIn( scope: CoroutineScope, started: SharingStarted, replay: Int = 0 ): SharedFlow<T> ``` It launches a coroutine in `scope` that collects the upstream once and re-emits items into a `SharedFlow`. `replay` is how many recent values are cached and replayed to new collectors. ## stateIn ```kotlin fun <T> Flow<T>.stateIn( scope: CoroutineScope, started: SharingStarted, initialValue: T\): StateFlow<T> ``` A `StateFlow` is a special `SharedFlow` that **always has a value** (`.value`), keeps exactly one latest value (`replay = 1`, conflated), and only emits when the value changes (distinct via `equals`). New collectors immediately get the current value. ## Why a scope and SharingStarted The shared upstream coroutine must live in some scope (e.g. `viewModelScope`). `SharingStarted` decides *when* that upstream coroutine actually starts and stops collecting — `Eagerly`, `Lazily`, or `WhileSubscribed(...)`. ## Key contrast - `shareIn` -> `SharedFlow`, replay configurable, no initial value, can have zero cached values. - `stateIn` -> `StateFlow`, always one current value, needs `initialValue`, conflates and de-duplicates.

  • Why does stateIn require an initialValue but shareIn does not?
    A StateFlow must always have a current value available via .value for any new collector, so it needs a seed before the upstream emits. SharedFlow can legitimately have zero cached values.
  • Does converting to hot change how many times the upstream runs?
    Yes — the upstream now runs at most once (shared) instead of once per collector, which is often the whole point (e.g. one network/db subscription).

Cold flow is cooking a fresh meal per guest; shareIn/stateIn is a buffet everyone shares from one kitchen.

saying these in an interview costs you the question

  • Saying shareIn and stateIn are interchangeable
  • Claiming the upstream still restarts per collector after conversion
  • Thinking StateFlow can exist without a current value
  • Forgetting that a CoroutineScope is required
  • Confusing replay count with initialValue

context