StateFlow

interface StateFlow<out T> : SharedFlow<T>

A SharedFlow that represents a read-only state with a single updatable data value that emits updates to the value to its collectors. A state flow is a hot flow because its active instance exists independently of the presence of collectors. Its current value can be retrieved via the value property.

State flow never completes. A call to Flow.collect on a state flow never completes normally, and neither does a coroutine started by the Flow.launchIn function. An active collector of a state flow is called a subscriber.

A mutable state flow is created using MutableStateFlow(value) constructor function with the initial value. The value of mutable state flow can be updated by setting its value property. Updates to the value are always conflated. So a slow collector skips fast updates, but always collects the most recently emitted value.

StateFlow is useful as a data-model class to represent any kind of state. Derived values can be defined using various operators on the flows, with combine operator being especially useful to combine values from multiple state flows using arbitrary functions.

For example, the following class encapsulates an integer state and increments its value on each call to inc:

class CounterModel {
private val _counter = MutableStateFlow(0) // private mutable state flow
val counter = _counter.asStateFlow() // publicly exposed as read-only state flow

fun inc() {
_counter.update { count -> count + 1 } // atomic, safe for concurrent use
}
}

Having two instances of the above CounterModel class one can define the sum of their counters like this:

val aModel = CounterModel()
val bModel = CounterModel()
val sumFlow: Flow<Int> = aModel.counter.combine(bModel.counter) { a, b -> a + b }

As an alternative to the above usage with the MutableStateFlow(...) constructor function, any coldFlow can be converted to a state flow using the stateIn operator.

Strong equality-based conflation

Values in state flow are conflated using Any.equals comparison in a similar way to distinctUntilChanged operator. It is used to conflate incoming updates to value in MutableStateFlow and to suppress emission of the values to collectors when new value is equal to the previously emitted one. State flow behavior with classes that violate the contract for Any.equals is unspecified.

State flow is a shared flow

State flow is a special-purpose, high-performance, and efficient implementation of SharedFlow for the narrow, but widely used case of sharing a state. See the SharedFlow documentation for the basic rules, constraints, and operators that are applicable to all shared flows.

State flow always has an initial value, replays one most recent value to new subscribers, does not buffer any more values, but keeps the last emitted one, and does not support resetReplayCache. A state flow behaves identically to a shared flow when it is created with the following parameters and the distinctUntilChanged operator is applied to it:

// MutableStateFlow(initialValue) is a shared flow with the following parameters:
val shared = MutableSharedFlow(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
shared.tryEmit(initialValue) // emit the initial value
val state = shared.distinctUntilChanged() // get StateFlow-like behavior

Use SharedFlow when you need a StateFlow with tweaks in its behavior such as extra buffering, replaying more values, or omitting the initial value.

StateFlow vs ConflatedBroadcastChannel

Conceptually, state flow is similar to ConflatedBroadcastChannel and is designed to completely replace it. It has the following important differences:

  • StateFlow is simpler, because it does not have to implement all the Channel APIs, which allows for faster, garbage-free implementation, unlike ConflatedBroadcastChannel implementation that allocates objects on each emitted value.

  • StateFlow always has a value which can be safely read at any time via value property. Unlike ConflatedBroadcastChannel, there is no way to create a state flow without a value.

  • StateFlow has a clear separation into a read-only StateFlow interface and a MutableStateFlow.

  • StateFlow conflation is based on equality like distinctUntilChanged operator, unlike conflation in ConflatedBroadcastChannel that is based on reference identity.

  • StateFlow cannot be closed like ConflatedBroadcastChannel and can never represent a failure. All errors and completion signals should be explicitly materialized if needed.

StateFlow is designed to better cover typical use-cases of keeping track of state changes in time, taking more pragmatic design choices for the sake of convenience.

To migrate ConflatedBroadcastChannel usage to StateFlow, start by replacing usages of the ConflatedBroadcastChannel() constructor with MutableStateFlow(initialValue), using null as an initial value if you don't have one. Replace send and trySend calls with updates to the state flow's MutableStateFlow.value, and convert subscribers' code to flow operators. You can use the filterNotNull operator to mimic behavior of a ConflatedBroadcastChannel without initial value.

Concurrency

All methods of state flow are thread-safe and can be safely invoked from concurrent coroutines without external synchronization.

Operator fusion

Application of flowOn, conflate, buffer with CONFLATED or RENDEZVOUS capacity, distinctUntilChanged, or cancellable operators to a state flow has no effect.

Implementation notes

State flow implementation is optimized for memory consumption and allocation-freedom. It uses a lock to ensure thread-safety, but suspending collector coroutines are resumed outside of this lock to avoid dead-locks when using unconfined coroutines. Adding new subscribers has O(1) amortized cost, but updating a value has O(N) cost, where N is the number of active subscribers.

Not stable for inheritance

The StateFlow interface is not stable for inheritance in 3rd party libraries, as new methods might be added to this interface in the future, but is stable for use. Use the MutableStateFlow(value) constructor function to create an implementation.

Properties

value
Link copied to clipboard
abstract val value: T

The current value of this state flow.

Inheritors

MutableStateFlow
Link copied to clipboard

Sources

common source
Link copied to clipboard