A biblioteca Paging monitora o estado das solicitações de carregamento dos dados paginados e os expõe
usando a classe LoadState.
Um sinal LoadState separado é fornecido para cada
LoadType e cada tipo de fonte de dados
(PagingSource ou
RemoteMediator). O objeto
CombinedLoadStates
fornecido pelo listener fornece informações sobre o estado de carregamento
de todos esses sinais. Use essas informações detalhadas para exibir os
indicadores de carregamento adequados aos usuários.
Estados de carregamento
A biblioteca Paging usa o objeto
LoadState para expor o estado de carregamento que será usado na IU. Os objetos LoadState podem assumir uma de três formas, dependendo do
estado de carregamento atual:
- Se não houver operação de carregamento ativa nem erros,
LoadStateserá um objetoLoadState.NotLoading. Essa subclasse também inclui a propriedadeendOfPaginationReached, que indica se o fim da paginação foi atingido. - Se houver uma operação de carregamento ativa,
LoadStateserá um objetoLoadState.Loading. - Se houver algum erro,
LoadStateserá um objetoLoadState.Error.
Acesse esses estados pela propriedade loadState do wrapper
LazyPagingItems. É possível usar esse estado de duas maneiras: processando
a visibilidade do conteúdo principal (como um ícone de carregamento em tela cheia) ou inserindo
itens de carregamento diretamente no stream LazyColumn (como um ícone de carregamento no rodapé).
Acessar o estado de carregamento usando um listener
Para monitorar o estado de carregamento na interface, use a propriedade loadState
fornecida pelo wrapper LazyPagingItems. Isso retorna um objeto
CombinedLoadStates que permite reagir ao comportamento de carregamento para
eventos de atualização, adição ao final ou adição ao início.
No exemplo a seguir, a IU mostra um ícone de carregamento ou uma mensagem de erro, dependendo do estado atual do carregamento de atualização (inicial):
@Composable fun UserListScreen(viewModel: UserViewModel) { val pagingItems = viewModel.flow.collectAsLazyPagingItems() Box(modifier = Modifier.fillMaxSize()) { // Show the list content LazyColumn { items(pagingItems.itemCount) { index -> UserItem(pagingItems[index]) } } // Handle the loading state when (val state = pagingItems.loadState.refresh) { is LoadState.Loading -> { CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) } is LoadState.Error -> { ErrorButton( message = state.error.message ?: "Unknown error", onClick = { pagingItems.retry() }, modifier = Modifier.align(Alignment.Center) ) } else -> {} // No separate view needed for success/not loading } } }
Para mais informações sobre LazyPagingItems, consulte Conjuntos de dados grandes (paginação).
Adicionar cabeçalhos e rodapés de carregamento
Para mostrar indicadores de carregamento no início ou no fim da lista (atuando como
cabeçalhos ou rodapés), adicione blocos de itens dedicados especificamente para esses estados
no escopo LazyColumn.
É possível monitorar o estado de adição ao início para o cabeçalho e o estado de adição ao final para o
rodapé usando o objeto CombinedLoadStates.
No exemplo a seguir, a lista mostra uma barra de progresso ou um botão "Repetir" na parte de baixo da lista quando mais dados estão sendo buscados:
@Composable fun UserList(viewModel: UserViewModel) { val pagingItems = viewModel.pager.flow.collectAsLazyPagingItems() LazyColumn { // 1. Header (Prepend state) // Useful if you support bidirectional paging or jumping to the middle item { val prependState = pagingItems.loadState.prepend if (prependState is LoadState.Loading) { LoadingItem() } else if (prependState is LoadState.Error) { ErrorItem( message = prependState.error.message ?: "Error", onClick = { pagingItems.retry() } ) } } // 2. Main Data items(pagingItems.itemCount) { index -> UserItem(pagingItems[index]) } // 3. Footer (Append state) // Shows when the user scrolls to the bottom and more data is loading item { val appendState = pagingItems.loadState.append if (appendState is LoadState.Loading) { LoadingItem() } else if (appendState is LoadState.Error) { ErrorItem( message = appendState.error.message ?: "Error", onClick = { pagingItems.retry() } ) } } } } @Composable fun LoadingItem() { Box(modifier = Modifier.fillMaxWidth().padding(16.dp), contentAlignment = Alignment.Center) { CircularProgressIndicator() } } @Composable fun ErrorItem(message: String, onClick: () -> Unit) { Column( modifier = Modifier.fillMaxWidth().padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = message, color = Color.Red) Button(onClick = onClick) { Text("Retry") } } }
Acessar informações adicionais sobre o estado de carregamento
Como mostrado nos exemplos anteriores, chamar pagingItems.loadState.refresh é
conveniente. No entanto, ele oculta a diferença entre o carregamento do banco de dados local (PagingSource) e da rede (RemoteMediator). Isso pode fazer com que a IU mostre brevemente um ícone de carregamento, mesmo quando os dados em cache estão disponíveis imediatamente.
Para um controle preciso, como mostrar um ícone de carregamento somente quando o banco de dados local
estiver vazio e uma sincronização de rede estiver ativa, acesse as propriedades source e mediator
diretamente no elemento de composição.
val loadState = pagingItems.loadState val isSyncing = loadState.mediator?.refresh is LoadState.Loading val isLocalEmpty = loadState.source.refresh is LoadState.NotLoading && pagingItems.itemSnapshotList.items.isEmpty() if (isSyncing && isLocalEmpty) { FullScreenLoading() } else { UserList(pagingItems) if (isSyncing) { TopOverlaySpinner() } }
Como pagingItems.loadState emite um novo objeto CombinedLoadStates sempre que
prepend ou append muda durante a rolagem, envolva a verificação de atualização em
derivedStateOf com remember(pagingItems). Isso impede que as atualizações de borda de rolagem
recomponham um escopo pai que apenas alterna um indicador de atualização
em tela cheia:
val showFullScreenLoading by remember(pagingItems) { derivedStateOf { // Avoid recomposing on prepend or append updates while scrolling, and // avoid covering existing items during a background refresh. pagingItems.loadState.source.refresh is LoadState.Loading && pagingItems.itemCount == 0 } } if (showFullScreenLoading) { Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { CircularProgressIndicator() } }
Reagir a mudanças de estado de carregamento
Talvez seja necessário acionar efeitos colaterais únicos com base em mudanças no estado de carregamento,
como rolar até o topo de uma lista ou mostrar um Snackbar quando uma atualização
for concluída ou falhar.
Use snapshotFlow dentro de um LaunchedEffect para observar as mudanças de estado como um
stream. Isso permite aplicar operadores Flow padrão, como filter,
distinctUntilChanged e distinctUntilChangedBy, para isolar
eventos específicos.
val listState = rememberLazyListState() LaunchedEffect(pagingItems) { // 1. Convert the state to a Flow snapshotFlow { pagingItems.loadState.refresh } // 2. Filter for the specific event (Refresh completed successfully) .distinctUntilChanged() .filter { it is LoadState.NotLoading } .collect { // 3. Trigger the side effect listState.animateScrollToItem(0) } }
Da mesma forma, é possível usar distinctUntilChangedBy em loadState para mostrar um
erro Snackbar somente quando o estado refresh mudar para
LoadState.Error:
val snackbarHostState = remember { SnackbarHostState() } // Key on pagingItems rather than loadState so state updates do not restart the // coroutine and reset distinctUntilChangedBy. LaunchedEffect(pagingItems) { snapshotFlow { pagingItems.loadState } // Ignore prepend and append updates so scrolling does not re-trigger the // refresh error Snackbar. .distinctUntilChangedBy { it.refresh } .filter { it.refresh is LoadState.Error } .collect { loadState -> val error = (loadState.refresh as LoadState.Error).error val result = snackbarHostState.showSnackbar( message = "Data failed to load: ${error.localizedMessage ?: "Unknown error"}", actionLabel = "Retry" ) if (result == SnackbarResult.ActionPerformed) { pagingItems.retry() } } }
Outros recursos
Para mais informações sobre a biblioteca Paging e os estados de carregamento, consulte os recursos abaixo.
Documentação
Visualiza conteúdo
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Visão geral da biblioteca Paging