When the first page results (20) are scrolled down and when there are new results from the next page the screen flickers/refreshes. Tried many things but no idea how to stop this screen flickering.
Screen:
@Composable
fun FilmsListScreen(
viewModel: FilmsListViewModel = hiltViewModel(),
selectedItem: (Long) -> Unit
) {
val filmState = viewModel.filmStateFlow.collectAsState().value
val isLoadingMore = viewModel.isLoadingMore
val gridState = rememberLazyGridState()
when (filmState) {
is ViewState.Success -> {
val list = filmState.data
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.padding(8.dp),
state = gridState
) {
items(list, key = { it.id }) { film ->
FilmGridCardUI(film, selectedItem)
}
if (isLoadingMore) {
item {
LoadingView()
}
} else {
item {
viewModel.loadNextPage()
}
}
}
}
is ViewState.Error -> {
ErrorView(reason = filmState.message)
}
else -> {}
}
}
ViewModel:
@HiltViewModel
class FilmsListViewModel @Inject constructor(
private val filmsListUseCase: FilmsListUseCase
) : ViewModel() {
private val _filmStateFlow: MutableStateFlow<ViewState<List<Film>>> =
MutableStateFlow(ViewState.Idle)
val filmStateFlow = _filmStateFlow.asStateFlow()
// Pagination state
private var currentPage = 1
var isLoadingMore = false
private var allFilms: MutableList<Film> = mutableListOf()
init {
getFilmsList(currentPage)
}
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun getFilmsList(page: Int = 1) {
viewModelScope.launch {
_filmStateFlow.value = ViewState.Loading
isLoadingMore = true
filmsListUseCase(page).collect {
when (it) {
is Result.Error -> {
_filmStateFlow.value = ViewState.Error(it.throwable.message ?: "error loading")
isLoadingMore = false
}
is Result.Success -> {
val filmsList = it.data
allFilms.addAll(filmsList)
_filmStateFlow.value = ViewState.Success(allFilms)
currentPage++
isLoadingMore = false
}
}
}
}
}
// Function to load the next page of films
fun loadNextPage() {
getFilmsList(currentPage)
}
}
FilmsListScreen
displays blank screen when filmState == ViewState.Loading
:
when (filmState) {
is ViewState.Success -> //...
is ViewState.Error -> //...
else -> {} // ViewState.Loading goes here
}
Judging by presence of LoadingView()
in FilmsListScreen
you want to display a loading indicator below the film items instead. To do that first you need to remove _filmStateFlow.value = ViewState.Loading
.
Then there is another issue - incorrect _filmStateFlow
updating. If ViewState.Success
is a data class
with a single property, doing this: _filmStateFlow.value = ViewState.Success(allFilms)
won't trigger an update, because
those two instances of ViewState.Success
contain the same list instance - allFilms
and therefore are considered equal. So you need a new List
instance, something like:
// no need to modify the list, we will replace it
private var allFilms: List<Film> = emptyList()
//....
val newList = allFilms + filmsList
_filmStateFlow.value = ViewState.Success(newList)
allFilms = newList