I am working with paging 3, everything work fine except initial loading state. I am adding withLoadStateFooter but it never show loading state at first call
Here is my implementation
Load State Adapter
class LoadStateAdapter (
private val retry: () -> Unit
): LoadStateAdapter<LoadStateViewHolder>() {
override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
holder.bindTo(loadState)
}
override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState
): LoadStateViewHolder {
return LoadStateViewHolder.create(parent, retry)
}
}
Load State View Holder
class LoadStateViewHolder(
view : View,
private val retryCallback: () -> Unit
) : RecyclerView.ViewHolder(view) {
private val progressBar = view.findViewById<ProgressBar>(R.id.progress_bar)
private val errorMsg = view.findViewById<TextView>(R.id.error_msg)
private val btnRetry = view.findViewById<Button>(R.id.retry_button)
.also {
it.setOnClickListener { retryCallback() }
}
private var loadState : LoadState? = null
companion object {
fun create(parent: ViewGroup, retryCallback: () -> Unit): LoadStateViewHolder {
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.network_state_item, parent, false)
return LoadStateViewHolder(
view,
retryCallback
)
}
}
fun bindTo(loadState: LoadState) {
this.loadState = loadState
btnRetry.isVisible = loadState !is LoadState.Loading
errorMsg.isVisible = loadState !is LoadState.Loading
progressBar.isVisible = loadState is LoadState.Loading
if (loadState is LoadState.Error){
errorMsg.text = loadState.error.localizedMessage
}
}
}
Paging Source
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Model> {
try {
// Load page 1 if undefined.
val currentPage = params.key ?: 0
val offset = currentPage * 50
val requestParams = hashMapOf<String, Any>()
requestParams.put("limit", 50)
requestParams.put("offset", offset)
val response = repository.getList(requestParams)
val isFinish = response.paging != null && response.paging!!.next == null
return LoadResult.Page(
data = response.data ?: mutableListOf(),
prevKey = null, // Only paging forward.
nextKey = if (isFinish) null else currentPage + 1
)
} catch (e: Exception) {
// Handle errors in this block
return LoadResult.Error(e)
}
}
View Model
val listPagingFlow = Pager(PagingConfig(pageSize = 50)) {
MyPagingSource(repository)
}.flow.cachedIn(viewModelScope)
Activity
val pagingAdapter = MyPagingAdapter()
list.apply {
setHasFixedSize(true)
adapter = pagingAdapter.withLoadStateFooter(
footer = LoadStateAdapter { pagingAdapter.retry() }
)
}
lifecycleScope.launch(Dispatchers.IO) {
viewModel.listPagingFlow.collectLatest { pagingData ->
pagingAdapter.submitData(pagingData)
}
}
MyPagingAdapter is simple PagingDataAdapter
In short; loading state works fine but it did not showing at first request. Can any one help?
Current version 3.0.0-alpha04
withLoadStateFooter returns a ConcatAdapter which concatenates results from original PagingDataAdapter with a LoadStateAdapter that listens to CombinedLoadState.append events. So it's not expected for it to return an item during initial load (loadType == REFRESH), and it was designed this way because it doesn't really make sense to show a "footer" before any items has loaded.
However, to achieve what you want you can simply create your own ConcatAdapter which mirrors the implementation of .withLoadStateFooter very closely:
val originalAdapter = MyPagingDataAdapter(...)
val footerLoadStateAdapter = MyFooterLoadStateAdapter(...)
addLoadStateListener { loadStates ->
// You need to implement some logic here to update LoadStateAdapter.loadState
// based on however you want to prioritize between REFRESH / APPEND load states.
// Something really basic might be:
// footerLoadStateAdapter.loadState = when {
// loadStates.refresh is NotLoading -> loadStates.append
// else -> loadStates.refresh
// }
footerLoadStateAdapter.loadState = ...
}
return ConcatAdapter(originalAdapter, footerLoadStateAdapter)
Using the footer LoadStateAdapter for showing initial loading will cause issues. The list will automatically be scrolled to the bottom as mentioned in the comments.
The way to go about this is to use two LoadStateAdapters, one to show the initial loading and the second to show loading when more items are being loaded.
fun <T : Any, V : RecyclerView.ViewHolder> PagingDataAdapter<T, V>.withLoadStateAdapters(
header: LoadStateAdapter<*>,
footer: LoadStateAdapter<*>
): ConcatAdapter {
addLoadStateListener { loadStates ->
header.loadState = loadStates.refresh
footer.loadState = loadStates.append
}
return ConcatAdapter(header, this, footer)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With