Hi, I'm new to use Flow so I confused when to use ...
# flow
r
Hi, I'm new to use Flow so I confused when to use regular
Flow
or
StateFlow
. Many codes expose
Flow
in repository, and convert to
StateFlow
in ViewModels like this.
Copy code
val userState: StateFlow<User?> = userRepository.userFlow.stateIn(viewModelScope, Eagerly, null)
This case always show null state on UI started, even if repository has initial value. This problem may be solved by exposing
StateFlow
instead of regular
Flow
in repository, is it bad practice?
n
even if repository has initial value.
Repository data usually comes from a web-service or database in which case it takes time to get an "initial value" so it's not really "initial" at all. What kind of repository do you have such that you think it has an initial value? Do you, for example, want a list to show as empty while data is loading or some similar default value while loading? If you changed the UI to show a spinner, would you need to make a change in your repository. If you need to change the repository in order to change the way data is presented, then that is not a great design.
g
This case always show null state on UI started, even if repository has initial value
It’s implementation detail, userFlow can expose StateFlow if it under it’s contract to provide default value (even if it null) The reason why you get null at the first place is because subscription is asynchronous, so first it will have null as default value in StateFlow, after that it will subscribe on flow and start collecting.
Nothing wrong with exposing StateFlow, but you just should understand that it’s a contract of this API now to provide default value, you can think about it as about interface: interface UserRepository { val userFlow: Flow<User> val currentUser: User? } If it’s fine, just use StateFlow But not every API have meaningful default value or value at all, many are asyncronous, so instead exposing something like StateFlow<T?> it’s better to expose Flow<T> and let client to convert it to StateFlow is needed with any default value depending on case
r
Yes, in my case, default value(null) is not meaningful value, it only means "data has not been already fetched". But how to avoid meaningless transition in ViewModel(and UI) without exposing StateFlow? With above example, UI always shows default value to Flow value transition at opening screen even if data has been already fetched (and cached by the repository). It may cause bad UX.
g
But how to avoid meaningless transition in ViewModel(and UI) without exposing StateFlow?
This transition is caused by asyncronous nature of subscription. There are 2 points which are asyncronous: stateIn and subscription on userState. In theory if you use Dispatchers.Main.immediate and do subscription on main thread it should be almost immediate (so not exactly, because you will still get default value)
you can mitigate it on UI level using loaders for example. But it really depends on UI how to handle it Or you can expose StateFlow after all
r
Thanks. You mean this transition itself is unavoidable, but doesn't matter in most case (subscribing on default dispatcher), is it right? I'll watch UI behavior on this transition again, and try to improve my UI logic first of all. Thank you.
g
You mean this transition itself is unavoidable
When you create StateFlow from Flow and immediately subscribe on it (so if eager subscription is slower than subscriber), yes, it’s correct
r
Thank you!