It seems that using `State` in `ViewModel` is much...
# compose
c
It seems that using
State
in
ViewModel
is much simpler than
LiveData
. Should I use
MutableState
in
ViewModel
? Is this correct?
👌 1
a
You might want to read this thread.
o
Having Compose API in ViewModel makes it less reusable
👍 2
The best seems to rely on
Flow
on ViewModel and
collectAsState
on Compose side
👍 2
(same goes against
LiveData
within VM to allow reusing
ViewModel
in Desktop Compose)
b
I agree, exposing
Flow
(or even`StateFlow` to avoid setting default values in when calling
collectAsState
) from ViewModel's makes them only Kotlin dependent, not Android. This is an architectural choice, it depends on the boundaries and where you want to set them.
n
haha I started that original thread. So to answer your question I’m still up in the air on this one. We sort of landed on exposing flows from the view model and collecting as state in the view. I’m not a huge fan as it pollutes the view + we do still have some snapshotState in the view model. There are also some scenarios where i want derived state in the viewmodel which i can’t do with flows. I was thinking of asking another question in this channel about it with some more detail to get peoples thoughts.
🙌 1
a
Of course it's correct. Have you read the thread above? There are more details there. As for the difference, basically you can think of
State
as the
LiveData
for compose. If your project is pure compose, there's no reason to use
LiveData
.
❤️ 1
c
@Albert Chang Yes, I have read it, but I still have questions. Thank you for your answers. In addition, I have the same question because I did not find the use of
MutableState
in the
ViewModel
in compose-samples.
a
z
If you’re talking about androidx ViewModel, isn’t that android-only anyway?
s
Personally, I wouldn't worry about the reusability of state consumption from a ViewModel without a concrete use case. The chance that you'll be able to reuse a ViewModel in another UI without refactoring is pretty low. If you're adding a state to be consumed by Compose and that's intended purpose, you'll find that it generally works better to use
State<T>
vs any async type. Then any writes during event handlers (e.g. onClick) will be correctly applied in-sync with the next composition, and other side effects that will generally make things more efficient / feel more performant. If you're adding State that is naturally async (e.g. reading from a network, database), then there is no compelling reason to use
State<T>
over
Flow
and whatever is the most natural would be the right choice. The big caveat about using
State<T>
is that it's not designed to be a stream of any kind or consumed outside of Compose, and is not a replacement for
Flow<T>
. If you intend to use it as a stream,
Flow
is a better choice.
👍 4
TL;DR it depends and any of the above are fine. There's no reason to avoid using
State<T>
in a ViewModel as long as it's intended for use by Compose. In some situations
State<T>
will offer more consistent and efficient behavior than other options when used in Compose.
Here's the section of the code lab that I went into this: https://developer.android.com/codelabs/jetpack-compose-state#8
z
Adam’s also mentioned the hot vs cold distinction between
State
and
Flow
that is kind of related to what you’re talking about I think.
s
It's more the sync vs async behavior.
State<T>
writes are always applied in the next composition after they're committed consistently and changes are batched together (so if you change 5 states before the next batch is applied, only one recomposition is triggered)
The async state holders are more "eventually the client will be notified" and this may become consistent several recompositions later, or in a situation where you change 5 async state holders the resolution may end up triggering 5 separate recompositions.
The details get more detailed, but you can think of writes to
State<T>
at the high level as a pending changes posted to a composition applier; when the batch is applied all pending changes are processed together and applied at once in a single recomposition. You don't have to do anything to wire this up between the various states, it's baked into how
State<T>
and compose interact.
TL;DR none of this matters unless you're writing tests that interact directly with the snapshot system, but it's fun to know 😉