I have two state flows. Is it possible to combine them and get new state flow? Logically it should be possible because both state flows have initials values, but as I see combine function returns just Flow and not StateFlow.
StateFlow is a state-holder observable flow that emits the current and new state updates to its collectors. The current state value can also be read through its value property. To update state and send it to the flow, assign a new value to the value property of the MutableStateFlow class.
So far I created function:
fun <T1, T2, R> combineState(
flow1: StateFlow<T1>,
flow2: StateFlow<T2>,
scope: CoroutineScope = GlobalScope,
sharingStarted: SharingStarted = SharingStarted.Eagerly,
transform: (T1, T2) -> R
): StateFlow<R> = combine(flow1, flow2) {
o1, o2 -> transform.invoke(o1, o2)
}.stateIn(scope, sharingStarted, transform.invoke(flow1.value, flow2.value))
You could use the combine
operator, and then use the stateIn
function for the Flow
that results from that.
From the stateIn
documentation in the kotlinx coroutines repository:
The stateIn
function "Converts a cold Flow
into a hot StateFlow
that is started in the given coroutine scope, sharing the most recently emitted value from a single running instance of the upstream flow with multiple downstream subscribers."
It's signature, as of the time of writing this, is:
fun <T> Flow<T>.stateIn(
scope: CoroutineScope,
started: SharingStarted,
initialValue: T
): StateFlow<T> (source)
So you should be able to do whatever transformations you need to your Flow
s, including combining them, and then ultimately use stateIn
to convert them back to a StateFlow
.
It could look something like this (perhaps creating a Scrabble game point calculator):
val wordFlow = MutableStateFlow("Hi")
val pointFlow = MutableStateFlow(5)
val stateString = wordFlow.combine(pointFlow) { word, points ->
"$word is worth $points points"
}.stateIn(viewModelScope, SharingStarted.Eagerly, "Default is worth 0 points")
stateString
would be of type StateFlow<String>
, and you have successfully combined the two other StateFlows
into one StateFlow
.
Similar solution to @Nikola Despotoski, but in a form of an extension function
/**
* Combines two [StateFlow]s into a single [StateFlow]
*/
fun <T1, T2, R> StateFlow<T1>.combineState(
flow2: StateFlow<T2>,
scope: CoroutineScope = GlobalScope,
sharingStarted: SharingStarted = SharingStarted.Eagerly,
transform: (T1, T2) -> R
): StateFlow<R> = combine(this, flow2) { o1, o2 -> transform.invoke(o1, o2) }
.stateIn(scope, sharingStarted, transform.invoke(this.value, flow2.value))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With