Yükleme durumlarını yönetme ve sunma

Paging kitaplığı, sayfalı veriler için yükleme isteklerinin durumunu takip eder ve bu durumu LoadState sınıfı üzerinden gösterir.

Her LoadType ve veri kaynağı türü (PagingSource veya RemoteMediator) için ayrı bir LoadState sinyali sağlanır. İşleyici tarafından sağlanan CombinedLoadStates nesnesi, tüm bu sinyallerden yükleme durumu hakkında bilgi sağlar. Bu ayrıntılı bilgileri kullanarak kullanıcılarınıza uygun yükleme göstergelerini gösterebilirsiniz.

Yükleme durumları

Paging kitaplığı, kullanıcı arayüzünde kullanılacak yükleme durumunu LoadState nesnesi aracılığıyla gösterir. LoadState nesneleri, mevcut yükleme durumuna bağlı olarak üç biçimden birini alır:

Bu durumlara LazyPagingItems sarmalayıcınızın loadState özelliği aracılığıyla erişin. Bu durumu iki şekilde kullanabilirsiniz: Ana içeriğin görünürlüğünü yönetme (tam ekran yenileme döndürücüsü gibi) veya yükleme öğelerini doğrudan LazyColumn akışınıza ekleme (altbilgi döndürücüsü gibi).

Yükleme durumuna bir dinleyiciyle erişme

Kullanıcı arayüzünüzdeki yükleme durumunu izlemek için LazyPagingItems sarmalayıcısı tarafından sağlanan loadState özelliğini kullanın. Bu, yenileme, ekleme veya başa ekleme etkinlikleri için yükleme davranışına tepki vermenizi sağlayan bir CombinedLoadStates nesnesi döndürür.

Aşağıdaki örnekte kullanıcı arayüzü, yenileme (ilk) yüklemesinin mevcut durumuna bağlı olarak bir yükleme döner simgesi veya hata mesajı gösterir:

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

LazyPagingItems hakkında daha fazla bilgi için Büyük veri kümeleri (sayfalama) başlıklı makaleyi inceleyin.

Listenizin başında veya sonunda yükleme göstergeleri görüntülemek (üstbilgi veya altbilgi olarak) için LazyColumn kapsamınızda özellikle bu durumlar için özel öğe blokları ekleyin.

CombinedLoadStates nesnesini kullanarak üstbilginin başa ekleme durumunu ve altbilginin sona ekleme durumunu izleyebilirsiniz.

Aşağıdaki örnekte, daha fazla veri getirilirken listenin alt kısmında bir ilerleme çubuğu veya yeniden deneme düğmesi gösterilmektedir:

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

Yükleme durumuyla ilgili ek bilgilere erişme

Önceki örneklerde gösterildiği gibi pagingItems.loadState.refresh'i aramak kolaydır. Ancak yerel veritabanınızdan (PagingSource) ve ağınızdan (RemoteMediator) yükleme arasındaki farkı gizler. Bu durum, önbelleğe alınmış veriler hemen kullanılabilir olsa bile kullanıcı arayüzünün kısa bir süre için yükleme animasyonu göstermesine neden olabilir.

Yerel veritabanı boşken ve ağ senkronizasyonu etkinken yükleme animasyonu göstermek gibi hassas kontrol için source ve mediator özelliklerine doğrudan composable'ınızdan erişin.

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

pagingItems.loadState, kaydırma sırasında prepend veya append her değiştiğinde yeni bir CombinedLoadStates nesnesi yayınladığından yenileme kontrolünü remember(pagingItems) ile derivedStateOf içine sarmalayın. Bu sayede, kaydırma kenarı güncellemelerinin yalnızca tam ekran yenileme göstergesini açıp kapatan bir üst kapsamı yeniden oluşturması önlenir:

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

Yükleme durumu değişikliklerine tepki verme

Yükleme durumu değişikliklerine göre tek seferlik yan etkileri tetiklemeniz gerekebilir. Örneğin, bir listenin en üstüne kaydırma veya yenileme tamamlandığında ya da başarısız olduğunda Snackbar gösterme gibi.

Durum değişikliklerini akış olarak gözlemlemek için LaunchedEffect içinde snapshotFlow kullanın. Bu sayede, belirli etkinlikleri ayırmak için filter, distinctUntilChanged ve distinctUntilChangedBy gibi standart Flow operatörlerini uygulayabilirsiniz.

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

Benzer şekilde, yalnızca refresh durumu LoadState.Error değerine geçtiğinde bir hata Snackbar göstermek için loadState üzerinde distinctUntilChangedBy kullanabilirsiniz:

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

Ek kaynaklar

Paging kitaplığı ve yükleme durumları hakkında daha fazla bilgi için aşağıdaki kaynakları inceleyin.

Belgeler

İçerik görüntüleme