Skip to main content

StateFlow, SharedFlow, and channels

StateFlow: current state​

private val _uiState = MutableStateFlow(UiState())
val uiState: StateFlow<UiState> = _uiState.asStateFlow()

StateFlow always has a current value, replays the latest value to new collectors, and conflates equal values using equality.

Update atomically when based on old state:

_uiState.update { old -> old.copy(count = old.count + 1) }

SharedFlow: broadcast values​

SharedFlow has configurable replay and buffer behavior. It is useful when several collectors should receive the same stream and there is no single current-state meaning.

Channel: send/receive queue​

A channel coordinates senders and receivers. Capacity controls whether send suspends, buffers, or replaces values. Closing a channel is meaningful; SharedFlow does not complete normally in the same way.

Events are a product decision​

Navigation, snackbars, and payments are not solved merely by choosing SharedFlow or Channel. Decide whether an event must survive collector absence, configuration change, process death, or duplicate delivery.

✕ Use state as a one-time event without consumption design

data class UiState(val navigateToDetails: Boolean)

✓ Model durable destination state or explicit event semantics

data class UiState(val selectedUserId: UserId?)

Comparison​

TypeHas current valueReplaysTypical meaning
StateFlowYesLatest oneObservable state
SharedFlowNo required current valueConfigurableBroadcast values/events
ChannelQueue semanticsReceive consumesPoint-to-point communication
Interview-ready answer

StateFlow represents current state and always has a value. SharedFlow broadcasts values with configurable replay/buffering. Channel provides send/receive queue semantics. Choose based on delivery guarantees, not naming convention.