कॉन्टेंट लोड होने की स्थितियों को मैनेज करना और प्रज़ेंट करना

पेजिंग लाइब्रेरी, पेज वाले डेटा के लिए लोड के अनुरोधों की स्थिति ट्रैक करती है. साथ ही, इसे LoadState क्लास के ज़रिए दिखाती है.

हर LoadType और डेटा सोर्स टाइप (या तो PagingSource या RemoteMediator) के लिए, अलग LoadState सिग्नल दिया जाता है. लिसनर से मिलने वाला CombinedLoadStates ऑब्जेक्ट, इन सभी सिग्नल से लोड होने की स्थिति के बारे में जानकारी देता है. इस विस्तृत जानकारी का इस्तेमाल करके, अपने उपयोगकर्ताओं को लोड होने के सही इंडिकेटर दिखाए जा सकते हैं.

लोड होने की स्थितियां

पेजिंग लाइब्रेरी, यूज़र इंटरफ़ेस (यूआई) में इस्तेमाल के लिए, LoadState ऑब्जेक्ट के ज़रिए लोड होने की स्थिति दिखाती है. LoadState ऑब्जेक्ट, लोड होने की मौजूदा स्थिति के हिसाब से, तीन में से कोई एक फ़ॉर्म लेते हैं:

  • अगर लोड करने की कोई कार्रवाई चालू नहीं है और कोई गड़बड़ी नहीं है, तो LoadState एक LoadState.NotLoading ऑब्जेक्ट होता है. इस सबक्लास में endOfPaginationReached प्रॉपर्टी भी शामिल होती है. इससे पता चलता है कि पेजिंग खत्म हो गई है या नहीं.
  • अगर लोड करने की कोई कार्रवाई चालू है, तो LoadState एक LoadState.Loading ऑब्जेक्ट होता है.
  • अगर कोई गड़बड़ी है, तो LoadState एक LoadState.Error ऑब्जेक्ट होता है.

इन स्थितियों को loadState रैपर की LazyPagingItems प्रॉपर्टी के ज़रिए, ऐक्सेस किया जा सकता है. इस स्थिति का इस्तेमाल दो तरीकों से किया जा सकता है: मुख्य कॉन्टेंट की दिखने की सेटिंग मैनेज करना (जैसे, पूरी स्क्रीन पर दिखने वाला रीफ़्रेश स्पिनर) या LazyColumn स्ट्रीम में सीधे लोड होने वाले आइटम डालना (जैसे, फ़ुटर स्पिनर).

लिसनर की मदद से, लोड होने की स्थिति को ऐक्सेस करना

अपने यूज़र इंटरफ़ेस (यूआई) में लोड होने की स्थिति पर नज़र रखने के लिए, loadState प्रॉपर्टी LazyPagingItems रैपर की दी गई का इस्तेमाल करें. इससे CombinedLoadStates ऑब्जेक्ट मिलता है. इसकी मदद से, रीफ़्रेश, अपेंड या प्रीपेंड इवेंट के लिए लोड होने के तरीके पर प्रतिक्रिया दी जा सकती है.

यहां दिए गए उदाहरण में, यूज़र इंटरफ़ेस (यूआई) में लोड होने का स्पिनर या गड़बड़ी का मैसेज दिखता है. यह इस बात पर निर्भर करता है कि रीफ़्रेश (शुरुआती) लोड की मौजूदा स्थिति क्या है:

@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 के बारे में ज़्यादा जानने के लिए, बड़े डेटासेट (पेजिंग) लेख पढ़ें.

अपनी सूची की शुरुआत या आखिर में लोड होने के इंडिकेटर दिखाने के लिए (हेडर या फ़ुटर के तौर पर काम करने वाले), LazyColumn स्कोप में उन स्थितियों के लिए खास तौर पर आइटम ब्लॉक जोड़ें.

`CombinedLoadStates` ऑब्जेक्ट का इस्तेमाल करके, हेडर के लिए प्रीपेंड स्थिति और फ़ुटर के लिए अपेंड स्थिति पर नज़र रखी जा सकती है.

यहां दिए गए उदाहरण में, ज़्यादा डेटा फ़ेच किए जाने पर, सूची में सबसे नीचे प्रोग्रेस बार या फिर से कोशिश करें बटन दिखता है:

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

लोड होने की स्थिति के बारे में ज़्यादा जानकारी ऐक्सेस करना

जैसा कि पहले के उदाहरणों में दिखाया गया है, pagingItems.loadState.refresh को कॉल करना आसान है. हालांकि, इससे स्थानीय डेटाबेस (PagingSource) और नेटवर्क (RemoteMediator) से लोड होने के बीच का अंतर छिप जाता है. इसकी वजह से, यूज़र इंटरफ़ेस (यूआई) में लोड होने का स्पिनर कुछ समय के लिए दिख सकता है. भले ही, कैश किया गया डेटा तुरंत उपलब्ध हो.

सटीक कंट्रोल के लिए, जैसे कि लोड होने का स्पिनर सिर्फ़ तब दिखाना, जब स्थानीय डेटाबेस खाली हो और नेटवर्क सिंक चालू हो, तो कंपोज़ेबल में सीधे source और mediator प्रॉपर्टी ऐक्सेस करें.

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

लोड होने की स्थिति में होने वाले बदलावों पर प्रतिक्रिया देना

आपको लोड होने की स्थिति में होने वाले बदलावों के आधार पर, एक बार होने वाले साइड इफ़ेक्ट ट्रिगर करने पड़ सकते हैं. जैसे, सूची में सबसे ऊपर स्क्रोल करना या रीफ़्रेश पूरा होने पर Snackbar दिखाना.

स्ट्रीम के तौर पर स्थिति में होने वाले बदलावों को देखने के लिए, LaunchedEffect में snapshotFlow का इस्तेमाल करें. इससे, खास इवेंट को अलग करने के लिए, स्टैंडर्ड Flow ऑपरेटर, जैसे कि filter और distinctUntilChanged लागू किए जा सकते हैं.

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

अन्य संसाधन

पेजिंग लाइब्रेरी और लोड होने की स्थितियों के बारे में ज़्यादा जानने के लिए, ये लेख पढ़ें.

दस्तावेज़

Views कॉन्टेंट