A biblioteca Paging monitora o estado das solicitações de carregamento dos dados paginados e os expõe
usando a LoadState classe.
Um sinal LoadState separado é fornecido para cada
LoadType e cada tipo de fonte de dados
(seja PagingSource ou
RemoteMediator). O
CombinedLoadStates
objeto 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á umLoadState.Errorobjeto.
Acesse esses estados pela loadState propriedade do
LazyPagingItems wrapper. Você pode usar esse estado de duas maneiras: processando a visibilidade do conteúdo principal (como um indicador de atualização de tela cheia) ou inserindo itens de carregamento diretamente no fluxo LazyColumn (como um indicador de rodapé).
Acessar o estado de carregamento usando um listener
Para monitorar o estado de carregamento na interface, use a loadState propriedade
fornecida pelo LazyPagingItems wrapper. Isso retorna um
CombinedLoadStates objeto que permite reagir ao comportamento de carregamento para
eventos de atualização, anexação ou inclusão.
No exemplo a seguir, a interface mostra um indicador 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.
Você pode monitorar o estado de inclusão para o cabeçalho e o estado de anexação para o rodapé usando o CombinedLoadStates objeto.
No exemplo a seguir, a lista mostra uma barra de progresso ou um botão "Tentar novamente" 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, isso oculta a diferença entre o carregamento do banco de dados local (PagingSource) e da rede (RemoteMediator). Isso pode fazer com que a interface mostre brevemente um indicador de carregamento, mesmo quando os dados armazenados em cache estão imediatamente disponíveis.
Para um controle preciso, como mostrar um indicador de carregamento apenas 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 combinável.
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() } }
Reagir a mudanças de estado de carregamento
Talvez seja necessário acionar efeitos colaterais únicos com base em mudanças de estado de carregamento, como rolar até o início de uma lista ou mostrar uma Snackbar quando uma atualização for concluída.
Use snapshotFlow dentro de um LaunchedEffect para observar as mudanças de estado como um fluxo. Isso permite aplicar operadores padrão Flow como filter
e distinctUntilChanged 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) } }
Outros recursos
Para mais informações sobre a biblioteca Paging e os estados de carregamento, consulte os recursos a seguir.
Documentação
Conteúdo de visualizações
Recomendados para você
- Observação: o texto do link aparece quando o JavaScript está desativado.
- Visão geral da biblioteca Paging