Hi all, I'm x-posting this from <#C01D6HTPATV|comp...
# compose
r
Hi all, I'm x-posting this from #compose-desktop since it's relatively generic to get a little more exposure. Could anyone try to help me understand something with
Flow
and
Compose
? One of the predominant lessons I've been learning is "make your composables stateless by hoisting". I'm trying to adhere to this by exposing as much state as makes sense through properties on my view models. For example, my data layer has an in-memory Repository that exposes a collection of items through a
Flow
. My view model is meant to
collect
that flow as state, and expose that state to my
Table
composable to be displayed on the screen. But since
collectAsState
is marked as
@Composable
I can't call it from my view model. As I see it I could either pass the
Flow
directly through my view model to my
Table
composable or stick with
mutableStateOf
and collect the
Flow
internally to the view model and just expose the
MutableState
to my
Table
. More generally, though, it feels like the two ideas "state through the view model" and "collectAsState is Composable-only" are at odds here, which makes me think there's "the way" to do this sort of thing that I'm not seeing right now.
For additional context on the original post here are some thoughts someone shared already:
Generally-speaking,
collectAsState()
is done to connect the reactive
Flow
into the UI/Compose world. ViewModels, by nature, live above the UI, and so should not be using
State<T>
, but instead still working with
Flow
.
So what you’d want to do is have your ViewModel process your Repository’s Flow in some manner, and then expose its own Flow (or StateFlow) which contains the data from the Repository and also the other stuff you’re managing in your ViewModel. And then your Compose code calls
collectAsState()
on your ViewModel’s Flow, and is completely ignorant of the Repository
b
We have lots of docs on this concept. It's a good pattern to have a stateful and stateless composable. So in your case you would have something like
Copy code
// Stateful
@Composable
fun ItemScreen(viewModel = viewModel()) {
   val items = viewModel.items.collectAsState()
   ItemContent(items)
}

// Stateless
fun ItemContent(items: List<Item>) {
   ...
}
The benefit is you can easily preview and test
ItemContent
with fake data
m
I agree with @Ben Trengrove [G] here for sure. We do exactly what he's suggesting here. Not only does this provide a true separation of concerns, we can also render it in any state we want in a screen gallery. In addition, we can also take advantage of this in snapshot testing.
r
Very interesting, thank you both for sharing! I think I misinterpreted all of the notes about hoisting state as "using stateful composables in any way isn't the right way", but instead it sounds like things are far less strict than that. I like the approach here and will give it a try!