androidandroid-jetpack-composekotlin-flowandroid-paging-3android-jetpack-compose-lazy-column

Compose LazyColumn with Paging3 scrolls up when item updated


I am using Paging3 with RemoteMediator for getting remote data, saving it in Room database and showing it in my Composable screen. On item click, opens detail screen and can update the data of that item saved in database. Whenever an item it's updated, because the paged data is collected as a Flow, it automatically updates de list.

The problem is that whenever an update is done in the detail view, once I go back, the list scrolls (without seeing the scrolling effect) up till the top of the list, like if the whole list was updated instead of just that item.

the screen:

@OptIn(ExperimentalMaterial3Api::class)
@SuppressLint("UnusedMaterial3ScaffoldPaddingParameter")

@Composable
fun ReposListScreen(navController: NavController,
                    viewModel: RepositoriesViewModel = hiltViewModel()) {

val repos = viewModel.data.collectAsLazyPagingItems()
val snackBarHostState = remember { SnackbarHostState() }
val coroutineScope = rememberCoroutineScope()

Scaffold(
    topBar = {
        TopAppBar(
            title = { Text(text = stringResource(id = R.string.str_best_rated_repos)) }
        )
    },
    snackbarHost = { SnackbarHost(snackBarHostState) },
    bottomBar = {
        BottomNavigationBarComponent() {
            coroutineScope.launch {
                snackBarHostState.showSnackbar("You pressed $it.")
            }
        }
    }
) {
    LazyColumn(
        modifier = Modifier.padding(paddingValues = it)
    ) {

        when (val appendState = repos.loadState.append) {
            is LoadState.Loading -> {
                item { LoadingNextPageItem(modifier = Modifier.fillMaxWidth()) }
            }

            is LoadState.Error -> {
                item {
                    ErrorMessage(
                        modifier = Modifier
                            .fillMaxWidth(),
                        message = appendState.error.localizedMessage.orEmpty(),
                        onClickRetry = { repos.retry() }
                    )
                }
            }

            else -> {}
        }

        items(count = repos.itemCount, key = repos.itemKey { it.id }) { index ->
            repos[index]?.let { repo ->
                RepoItem(
                    repo,
                    onRepoClicked = {
                        navController.navigate(Routes.RepoDetailScreen.createRoute(repo.id))
                    }
                )
            }
        }

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

}

the viewmodel:

@HiltViewModel
class RepositoriesViewModel @Inject constructor(
    private val getPagedBestRatedReposUseCase: GetPagedBestRatedReposUseCase
) : ViewModel() {
private var _state: MutableStateFlow<UiState> = MutableStateFlow(UiState(loading = true))
val state: StateFlow<UiState> = _state.asStateFlow()

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

init {
    viewModelScope.launch(Dispatchers.IO) {
        _state.value = UiState(loading = true)
        getPagedBestRatedReposUseCase()
            .cachedIn(viewModelScope)
            .catch {
                _state.update {
                    it.copy(error = true, loading = false)
                }
            }
            .collect { repos ->
                _state.update {
                    it.copy(
                        dataSource = repos,
                        loading = false, error = false
                    )
                }
                _data.update { repos }
            }
    }
}

}

THE ANSWER BELOW

Okey, I found the answer thanks to @BenjyTec comment, that guided me till this issue: Google Issue

The thing is that val data = viewModel.data.collectAsLazyPagingItems() should be called before the initialization of the NavHost, and I was calling it in a composable screen called inside NavHost.


Solution

  • Okey, I found the answer thanks to @BenjyTec comment, that guided me till this issue: Google Issue

    The thing is that val data = viewModel.data.collectAsLazyPagingItems() should be called before the initialization of the NavHost, and I was calling it in a composable screen called inside NavHost.