Reactive Programming in Kotlin - StateFlow
The final Flow type to cover is the StateFlow
, which is a child of SharedFlow
. To learn more about SharedFlow
and specifically what a "hot" flow is, check out the previous article here. Now to dive into what makes StateFlow
special.
StateFlow is a specific SharedFlow #
Unlike the differences between a Flow
and a SharedFlow
, SharedFlow
and StateFlow
have a lot in common. In fact you can make your own StateFlow
by setting up a SharedFlow
with a very specific set of properties. First set the replay value to 1 so only the single most recent value is re-emitted to subscribers. Next, set the buffer policy to DROP_OLDEST
so the only item in the buffer is the current value. Then immediately tryEmit
the initial value. Finally, apply the distinctUntilChanged()
operator.
This setup covers the most important things to know about a StateFlow
- Holds onto a single value that is replayed to subscribers
- Always has a value, either it's initial value or the most recent updated value
- New values only notify subscribers if they are distinct from the current value
Using a StateFlow #
A StateFlow
is created using the MutableStateFlow
constructor or by using stateIn()
applied to a Flow
.
val mutable = MutableStateFlow(initialValue = "Hello")
val stateFlow = mutable.asStateFlow()
val myFlow = flowOf(1,2,3)
val flowSharedFlow = myFlow.stateIn(
scope = coroutineScope,
started = SharingStarted.Lazily,
initialValue = "Hello"
)
Since the buffer policy is a drop policy instead of a suspend policy, there is no tryEmit
vs emit
. Instead the value
is updated either directly or using the .update
functions. Both functions work very similarly but .update
and its siblings (updateAndGet
and getAndUpdate
) are atomic operations on the value, preventing tricky race conditions.
val myFlow = MutableStateFlow("Hello")
myFlow.value = "Good Bye"
myFlow.update { current ->
// New value is result of this block
current + " for now!"
}
myFlow.value // "Good Bye for now!"
Just like all other Flow types, a StateFlow
can be collected using a terminal operator, but additionally the latest value can be accessed using the .value
property.
val stateFlow = MutableStateFlow("Hello").asStateFlow()
// Subscribe to value
stateFlow.collect { ... }
// Get the current value one time
val currentValue = stateFlow.value
Conclusion #
All the features of StateFlow
make it an excellent option for managing state and is the preferred object for exposing state to consume in Jetpack Compose. A single current value that can be easily accessed combined with the power of reactivity makes StateFlow
a bit of the best of both worlds. This is the last post on this topic for now, but feel free to email me if there are questions or future topics of interest in this area. Until next time, thanks!
Did you find this content helpful?
Please share this post and be sure to subscribe to the RSS feed to be notified of all future articles!
Want to go above and beyond? Help me out by sending me $1 on Ko-fi. It goes a long way in helping run this site and keeping it advertisement free. Thank you in advance!
- Previous: Reactive Programming in Kotlin - SharedFlow
- Next: Dev Diary - November 2023