Mark
12/30/2023, 5:58 AMFlow
to a StateFlow
without using a CoroutineScope
. The idea is that the underlying Flow
is only collected when the StateFlow
is collected. Before that, the StateFlow
would just act as an initial state holder. Is this possible?Sam
12/30/2023, 9:49 AMMutableStateFlow
. In the upstream flow, add an onEach
block that updates the MutableStateFlow
.Sam
12/30/2023, 9:50 AMefemoney
12/30/2023, 9:51 AMStateFlow
subclass. (even though its not “safe” for extesion)efemoney
12/30/2023, 9:52 AMclass DerivedStateFlow<T>(
private val getValue: () -> T,
private val flow: Flow<T>,
) : StateFlow<T> {
override val replayCache: List<T>
get() = listOf(value)
override val value: T
get() = getValue()
@InternalCoroutinesApi
override suspend fun collect(collector: FlowCollector<T>): Nothing {
flow.distinctUntilChanged().conflate().collect(collector)
awaitCancellation()
}
}
(credit @billjings)Mark
12/30/2023, 10:02 AMgetValue()
here. It seems to be totally unrelated to flow
. I would expect an initialValue: T
to be passed in. And the collect() to return that value first. value
would initially return initialValue
and then the values as collected from flow
.efemoney
12/30/2023, 10:03 AMefemoney
12/30/2023, 10:06 AM{ otherStateFlow.map{…} }
. )Mark
12/30/2023, 10:06 AMclass DerivedStateFlow<T>(
private val initialValue: T,
private val flow: Flow<T>,
) : StateFlow<T> {
override val replayCache: List<T>
get() = listOf(_value)
private var _value: T = initialValue
override val value: T
get() = _value
override suspend fun collect(collector: FlowCollector<T>): Nothing {
flow {
emit(initialValue)
emitAll(flow)
}.distinctUntilChanged()
.conflate()
.onEach { _value = it }
.collect(collector)
awaitCancellation()
}
}
franztesca
12/30/2023, 12:08 PMMark
12/30/2023, 2:05 PMMutableStateFlow
is in this case. Surely you would want a StateFlow
only, in which case just use stateIn
franztesca
01/01/2024, 11:51 AMBefore that, theIf you don't need it to be mutable, then using the built-inwould just act as an initial state holderStateFlow
stateIn
sounds like a better option.Mark
01/02/2024, 3:23 AMby
) can lead to uiStateState
. Or is that just me?uli
01/02/2024, 11:14 AMclass FlowStateFlow<T> private constructor(
private val flow: Flow<T>,
private val stateFlow: MutableStateFlow<T>,
) : StateFlow<T> by stateFlow {
constructor(
flow: Flow<T>,
initial: T,
) : this(flow, MutableStateFlow(initial))
override suspend fun collect(collector: FlowCollector<T>): Nothing {
coroutineScope {
launch {
flow.collect {
stateFlow.value = it
}
}
stateFlow.collect {
collector.emit(it)
}
}
}
}
You might want to launch
on Dispatchers.Default
to collect the original flow in the background. I am just not sure if it is worth it. Would be interesting to see the behaviour under load.