Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine two state flows into new state flow

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.

like image 513
Kiryl Tkach Avatar asked Dec 24 '20 23:12

Kiryl Tkach


People also ask

What is mutable state flow?

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.


3 Answers

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))
like image 104
Kiryl Tkach Avatar answered Oct 17 '22 05:10

Kiryl Tkach


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 Flows, 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.

like image 21
Merrillogic Avatar answered Oct 17 '22 05:10

Merrillogic


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))
like image 1
tomrozb Avatar answered Oct 17 '22 03:10

tomrozb