3

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)) } } } 

2 Answers 2

2

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() 
Sign up to request clarification or add additional context in comments.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.
1

For me, the suggestion to use remember(state.dataSource) was too glitchy. I would see my "empty results" page flash as the page was loading. The scrolling was also not very smooth and would jump around if flung.

I ended up going with a simpler solution of a separate collection of the uiState object where I could map my PagingData to the flow. My code looks similar to:

val state by viewModel.state.collectAsState() val pagingItems = viewModel.state.map{ it.dataSource }.collectAsLazyPagingItems() 

Not ideal as the state object is collected twice. But still limits the viewModel exposed API to a single state object.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.