Collect PagingData from UiState object in ViewModel with Compose

113 Views Asked by At

Having ViewModel with this code:

private var _data: MutableStateFlow<PagingData<Pokemon>> = MutableStateFlow(PagingData.empty())
val data: StateFlow<PagingData<Pokemon>> = _data.asStateFlow()

allows me to get the paged data by calling the following in the composable:

val pagingItems = viewModel.data.collectAsLazyPagingItems()

the thing is, that I have my data inside a state/data object like this:

data class UiState(
    var loading: Boolean = false,
    var error: Boolean = false,
    val dataSource: PagingData<Pokemon> = PagingData.empty(),
)

and my ViewModel then has:

private var _state: MutableStateFlow<UiState> = MutableStateFlow(UiState(loading = true))
val state: StateFlow<UiState> = _state.asStateFlow()

How do I collect the dataSource of the UiState then in my composable? I am using the following but whenever the RemoteMediator is loading new data, the list scrolls up to almost the top.

//Not working, scrolling list to top when paging
val state by viewModel.state.collectAsState()
val pagingItems = flowOf(state.dataSource).collectAsLazyPagingItems()

and the screen:

@Composable
fun PokemonListScreen(navController: NavController, pokemon: LazyPagingItems<Pokemon>) {

    LazyColumn(
        modifier = Modifier.padding(4.dp)
    ) {
        item { Spacer(modifier = Modifier.padding(4.dp)) }

        items(count = pokemon.itemCount, key = pokemon.itemKey { it.id }) { index ->
            pokemon[index]?.let { poke ->
                PokemonItem(
                    poke,
                    onRepoClicked = {
                        navController.navigate(Routes.RepoDetailScreen.route)
                    }
                )
            }
        }


        pokemon.apply {
            when {
                loadState.refresh is LoadState.Loading -> {
                    item { PageLoader(modifier = Modifier.fillParentMaxSize()) }
                }

                loadState.refresh is LoadState.Error -> {
                    val error = pokemon.loadState.refresh as LoadState.Error
                    item {
                        ErrorMessage(
                            modifier = Modifier.fillParentMaxSize(),
                            message = error.error.localizedMessage,
                            onClickRetry = { retry() })
                    }
                }

                loadState.refresh is LoadState.NotLoading && pokemon.itemCount > 0 -> {
                    item {
                        ErrorMessage(
                            modifier = Modifier.fillParentMaxSize(),
                            message = stringResource(id = R.string.str_no_results),
                            onClickRetry = { retry() })
                    }
                }

                loadState.append is LoadState.Loading -> {
                    item { LoadingNextPageItem(modifier = Modifier) }
                }

                loadState.append is LoadState.Error -> {
                    val error = pokemon.loadState.append as LoadState.Error
                    item {
                        ErrorMessage(
                            modifier = Modifier,
                            message = error.error.localizedMessage,
                            onClickRetry = { retry() })
                    }
                }
            }
        }
        item { Spacer(modifier = Modifier.padding(4.dp)) }
    }

}
1

There are 1 best solutions below

1
Kamal Hassoun On

Every time your view recomposes this line is causing the issue...

val pagingItems = flowOf(state.dataSource).collectAsLazyPagingItems()

Try replacing it with this:

val pagingItems = remember(state.dataSource) {
        flow {
            emit(state.dataSource)
        }
    }.collectAsLazyPagingItems()