I am trying to implement android MVI architecture using state flow and paging 3 but I got confused when I had a view state which contains paging data.
The problem is that I expose the view state from view model as a state flow object, but now inside that view state I have another flow object which comes from the paging library.
Is it OK to have a flow inside a state flow? and if it’s not what should I do instead?
This is my code for more clarification.
TaskRepository
override fun list(
pageNumber: Int,
pageSize: Int,
groupId: Long?,
query: String
): Flow<PagingData<Task>> {
return Pager(
config = PagingConfig(
pageSize = Consts.PageSize,
maxSize = 200,
enablePlaceholders = false
),
remoteMediator = TaskRemoteMediator(query, groupId, db, taskApi),
pagingSourceFactory = {
TaskDataSource(taskApi, groupId, query)
}
).flow
}
TaskViewModel
viewModelScope.launch {
try {
_taskListViewState.emit(TaskListViewState.Loading)
val tasks = taskRepo.list(1, Consts.PageSize, intent.groupId, "")
_taskListViewState.emit(TaskListViewState.Data(tasks))
} catch (e: Exception) {
_taskListViewState.emit(TaskListViewState.Error(R.string.unknown_error))
}
}
TaskListViewState
sealed class TaskListViewState {
object Idle : TaskListViewState()
object Loading : TaskListViewState()
data class Data(val tasks: Flow<PagingData<Task>>) : TaskListViewState()
data class Error(val error: Int) : TaskListViewState()
}
TaskListFragment
private fun observeViewState() {
lifecycleScope.launchWhenStarted {
viewModel.taskListViewState.collect {
render(it)
}
}
}
private fun render(viewState: TaskListViewState) {
Log.d(TAG, "render: $viewState")
when (viewState) {
is TaskListViewState.Loading -> showLoading()
is TaskListViewState.Idle -> hildeLoading()
is TaskListViewState.Error -> {
hildeLoading()
showMessage(viewState.error)
}
is TaskListViewState.Data -> {
hildeLoading()
lifecycleScope.launchWhenCreated {
viewState.tasks.collectLatest {
tasksAdapter.submitData(lifecycle, it)
}
}
}
}
}
A bit late but you can modify the classes like below and you wouldn't need flow inside the TaskListViewState.
TaskListViewState
sealed class TaskListViewState {
object Idle : TaskListViewState()
object Loading : TaskListViewState()
data class Data(val tasks: PagingData<Task>) : TaskListViewState()
data class Error(val error: Int) : TaskListViewState()
}
TaskViewModel
viewModelScope.launch {
try {
_taskListViewState.emit(TaskListViewState.Loading)
taskRepo.list(1, Consts.PageSize, intent.groupId, "")
.cacheIn(viewModelScope)
.collectLatest { pagingData ->
_taskListViewState.emit(TaskListViewState.Data(pagingData))
}
} catch (e: Exception) {
_taskListViewState.emit(TaskListViewState.Error(R.string.unknown_error))
}
}
TaskListFragment
private fun observeViewState() {
lifecycleScope.launchWhenStarted {
viewModel.taskListViewState.collect {
render(it)
}
}
}
private fun render(viewState: TaskListViewState) {
Log.d(TAG, "render: $viewState")
when (viewState) {
is TaskListViewState.Loading -> showLoading()
is TaskListViewState.Idle -> hildeLoading()
is TaskListViewState.Error -> {
hildeLoading()
showMessage(viewState.error)
}
is TaskListViewState.Data -> {
hildeLoading()
tasksAdapter.submitData(lifecycle, viewState.tasks)
}
}
}