La biblioteca de Paging hace un seguimiento del estado de las solicitudes de carga de los datos paginados y lo muestra a través de la clase LoadState.
Se proporciona un indicador independiente de LoadState para cada LoadType y tipo de fuente de datos (PagingSource o RemoteMediator). El objeto CombinedLoadStates, que proviene del objeto de escucha, proporciona información sobre el estado de carga de todos estos indicadores. Puedes usar esta información detallada para mostrar los indicadores de carga correspondientes a tus usuarios.
Estados de carga
La biblioteca de Paging expone el estado de carga para usar en la IU mediante el objeto LoadState. Los objetos LoadState toman una de las siguientes tres formas según el estado de carga actual:
- Si no hay una operación de carga activa ni un error,
LoadStatees un objetoLoadState.NotLoading. Esta subclase también incluye la propiedadendOfPaginationReached, que indica si se completó la paginación. - Si hay una operación de carga activa,
LoadStatees un objetoLoadState.Loading. - Si hay un error,
LoadStatees un objetoLoadState.Error.
Accede a estos estados a través de la propiedad loadState de tu wrapper LazyPagingItems. Puedes usar este estado de dos maneras: controlando la visibilidad del contenido principal (como un spinner de actualización de pantalla completa) o insertando elementos de carga directamente en tu transmisión de LazyColumn (como un spinner de pie de página).
Cómo acceder al estado de carga mediante un objeto de escucha
Para supervisar el estado de carga en tu IU, usa la propiedad loadState proporcionada por el wrapper LazyPagingItems. Devuelve un objeto CombinedLoadStates que te permite reaccionar al comportamiento de carga de los eventos de actualización, anexión o anteposición.
En el siguiente ejemplo, la IU muestra un ícono giratorio de carga o un mensaje de error según el estado actual de la carga de actualización (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 obtener más información sobre LazyPagingItems, consulta Conjuntos de datos grandes (paging).
Agrega encabezados y pies de página de carga
Para mostrar indicadores de carga al principio o al final de la lista (que actúan como encabezados o pies de página), agrega bloques de elementos dedicados específicamente para esos estados dentro de tu alcance de LazyColumn.
Puedes supervisar el estado de la anteposición del encabezado y el estado de la posposición del pie de página con el objeto CombinedLoadStates.
En el siguiente ejemplo, la lista muestra una barra de progreso o un botón de reintentar en la parte inferior cuando se recuperan más datos:
@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") } } }
Cómo acceder a información adicional sobre el estado de carga
Como se muestra en los ejemplos anteriores, llamar a pagingItems.loadState.refresh es conveniente. Sin embargo, oculta la diferencia entre la carga desde tu base de datos local (PagingSource) y tu red (RemoteMediator). Esto puede hacer que la IU muestre brevemente un ícono giratorio de carga incluso cuando los datos almacenados en caché están disponibles de inmediato.
Para un control preciso, como mostrar un indicador de carga solo cuando la base de datos local está vacía y hay una sincronización de red activa, accede a las propiedades source y mediator directamente dentro de tu elemento componible.
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() } }
Cómo reaccionar a los cambios en el estado de carga
Es posible que debas activar efectos secundarios únicos según los cambios en el estado de carga, como desplazarte a la parte superior de una lista o mostrar un Snackbar cuando se completa una actualización.
Usa snapshotFlow dentro de un LaunchedEffect para observar los cambios de estado como una transmisión. Esto te permite aplicar operadores Flow estándar, como filter y distinctUntilChanged, para aislar 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) } }
Recursos adicionales
Para obtener más información sobre la biblioteca de Paging y los estados de carga, consulta los siguientes recursos.
Documentación
Mira contenido
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Descripción general de la biblioteca de Paging