Mengelola dan menampilkan status pemuatan

Libary Paging melacak status permintaan pemuatan untuk data yang di-page dan menampilkannya melalui class LoadState.

Sinyal LoadState terpisah diberikan untuk setiapLoadType dan jenis sumber data (PagingSource atau RemoteMediator). Objek CombinedLoadStates yang diberikan oleh pemroses berisi informasi tentang status pemuatan dari semua sinyal ini. Anda dapat menggunakan informasi mendetail ini untuk menampilkan indikator pemuatan yang sesuai untuk pengguna Anda.

Status pemuatan

Library Paging menampilkan status pemuatan untuk digunakan di UI melalui objek LoadState. Objek LoadState mengambil salah satu dari tiga bentuk, bergantung pada status pemuatan saat ini:

  • Jika tidak ada operasi pemuatan yang aktif dan tidak ada error, LoadState adalah objek LoadState.NotLoading. Subclass ini juga menyertakan properti endOfPaginationReached, yang menunjukkan apakah akhir penomoran halaman telah tercapai.
  • Jika ada operasi pemuatan yang aktif, LoadState adalah objek LoadState.Loading.
  • Jika ada error, LoadState adalah objek LoadState.Error.

Akses status ini melalui properti loadState dari wrapper LazyPagingItems Anda. Anda dapat menggunakan status ini dengan dua cara: menangani visibilitas konten utama (seperti spinner pemuatan ulang layar penuh) atau menyisipkan item pemuatan langsung ke aliran LazyColumn (seperti spinner footer).

Mengakses status pemuatan dengan pemroses

Untuk memantau status pemuatan di UI Anda, gunakan properti loadState yang disediakan oleh wrapper LazyPagingItems. Objek ini menampilkan objek CombinedLoadStates yang memungkinkan Anda bereaksi terhadap perilaku pemuatan untuk peristiwa muat ulang, tambahkan, atau tambahkan di depan.

Dalam contoh berikut, UI menampilkan indikator lingkaran berputar untuk pemuatan atau pesan error bergantung pada status pemuatan refresh (awal) saat ini:

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

Untuk mengetahui informasi selengkapnya tentang LazyPagingItems, lihat Set data besar (paging).

Untuk menampilkan indikator pemuatan di awal atau akhir daftar (bertindak sebagai header atau footer), tambahkan blok item khusus untuk status tersebut dalam cakupan LazyColumn.

Anda dapat memantau status penambahan untuk header dan status penambahan untuk footer menggunakan objek CombinedLoadStates.

Dalam contoh berikut, daftar menampilkan status progres atau tombol coba lagi di bagian bawah daftar saat lebih banyak data diambil:

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

Mengakses informasi status pemuatan tambahan

Seperti yang ditunjukkan dalam contoh sebelumnya, memanggil pagingItems.loadState.refresh sangat praktis. Namun, hal ini mengaburkan perbedaan antara pemuatan dari database lokal (PagingSource) dan jaringan (RemoteMediator). Hal ini dapat menyebabkan UI menampilkan indikator lingkaran berputar pemuatan secara singkat meskipun data yang di-cache langsung tersedia.

Untuk kontrol yang presisi, seperti menampilkan indikator lingkaran berputar pemuatan hanya saat database lokal kosong dan sinkronisasi jaringan aktif, akses properti source dan mediator secara langsung dalam composable Anda.

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

Merespons perubahan status pemuatan

Anda mungkin perlu memicu efek samping satu kali berdasarkan perubahan status pemuatan, seperti men-scroll ke bagian atas daftar atau menampilkan Snackbar saat penyegaran selesai.

Gunakan snapshotFlow di dalam LaunchedEffect untuk mengamati perubahan status sebagai aliran. Hal ini memungkinkan Anda menerapkan operator Flow standar seperti filter dan distinctUntilChanged untuk mengisolasi peristiwa tertentu.

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

Referensi lainnya

Untuk mengetahui informasi selengkapnya tentang library Paging dan status pemuatan, lihat referensi berikut.

Dokumentasi

Melihat konten