Is it possible to have a `StateFlow<T>` whos...
# coroutines
z
Is it possible to have a
StateFlow<T>
whose initial value
T
is lazily initialized, or asynchronously initialized? For example, let’s say
T
needs to be initially loaded from disk. I would like that
T
to be loaded asynchronously or lazily once used, and off the main thread
s
I'd make the initial state 'None' or 'Loading' or something like that.
Instead of StateFlow<T> do StateFlow<Container<T>> Where Container<T> is like
sealed class Container<out T> { object Loading: Container<Nothing>; data class Value<T>(private value: T): Container<T>
z
So what you're saying is it's not possible lol
s
Not with pure T, since a StateFlow must have an initial state.
Otherwise it would be just a SharedFlow
Note sure if it would work in your use case, but you can emit an 'initial' value of a (cold) Flow through its onStart method. Something like this:
Copy code
fun <T> Flow<T>.withInitialValue<T>(getInitialValue: suspend ()-> T) : Flow<T> { 
  return this.onStart { emit(getInitialValue()) }
}
^^^ but if the original Flow is a StateFlow with a value already or already has emitted one or more values, the return of getInitialValue() will be emitted as well.
z
For what you’re asking for to work,
value
would have to be a suspending property, which would defeat the purpose.
☝️ 1
z
It could asynchronously set the initial value, or blocking set it. The use case is loading the theme from disk for an app, and it's a million times easier to provide that with blocking code. I guess I should just take the L and do the blocking read for the initial value, but it would be cool if somehow that value could be asynchronously set in the background, but if
state.value
is invoked before the async read finishes, then do that blocking read
z
That non determinism would make it a lot harder to reason about the performance of StateFlow. And coroutine APIs should never block for IO, because it would defeat the point of coroutines.
s
Make a 'global' StateFlow with
Loading
(or something similar) as the initial state and that then will fetch and emit the Theme. Start loading that theme asap. Apply a
filter
to that StateFlow that filters on the Theme value (filters out the Loading one). Make a method that returns that filtered flow Then when you need that Theme in a screen, call that method returning that filtered flow, call
first()
on it that then returns Theme. Combine that with the rest of your UI-state emissions.