I am creating a screen that presents a list of items. This list of items come from the internet in a paginated response, so I am trying to use PagingData and Pager to do it, but just can’t find the correct way to pass that flow into the composable while mantaining MVI architecture.
The problem is that I am saving my UIState into savedStateHandle, but I can’t add a flow to the UIState data class because this flow isn’t parcelable.
This is my viewmodel (there is a bit of code missing but is not important for the question):
class PopularMoviesViewModel(
savedStateHandle: SavedStateHandle,
private val movieRepository: MovieRepository
) : ViewModel() {
private val pageSize = 20
data class UIState(
val isLoading: Boolean,
val movies: Flow<PagingData<Movie>>
)
private val _uiStateDelegate = ViewModelPropertyDelegate<PopularMoviesViewModel, UIState>(
savedStateHandle,
"uiState",
UIState(
false,
flow {}
)
)
private var _uiState by _uiStateDelegate
val uiState = _uiStateDelegate.getStateFlow()
sealed interface Action {
class OnClickMovie(val movie: Movie) : Action
}
init {
loadPopularMovies()
}
private fun loadPopularMovies() {
_uiState = _uiState.copy(movies = Pager(
config = PagingConfig(pageSize = pageSize),
initialKey = 1,
pagingSourceFactory = { movieRepository.getPopularMovies() }
).flow.cachedIn(viewModelScope))
}
}
This is my composable (again this is not the exact code but you get the idea):
@Composable
fun PopularMoviesComposable(
uiState: PopularMoviesViewModel.UIState,
doAction: (PopularMoviesViewModel.Action) -> Unit,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.fillMaxSize()
.padding(vertical = 48.dp, horizontal = 16.dp)
) {
val movies = uiState.movies.collectAsLazyPagingItems()
LazyVerticalGrid(
columns = GridCells.Adaptive(128.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
items(movies.itemCount)
{ index ->
movies[index]?.let {
MovieCard(
movie = it,
onClickMovie = {doAction(PopularMoviesViewModel.Action.OnClickMovie(it))}
)
}
}
}
}
}
As you can see the composable doesn’t get the viewmodel as a parameter, it gets the UIState, so the question is
Is it possible to have a composable with an UIState class like this if the UIState class has a flow in it?
Sorry for bad english.
I know I can just inject the viewmodel into the composable instead of using an UIState and then get everything I need from there. The thing is that when testing composables and the viewmodel I find that this aproach of using an UIState data class is muuch easier to implement.
Maybe the aproach itself is not correct and I’m not understanding MVI correctly?