Paging3 Dynamic filtering not working as expected

33 Views Asked by At

I am currently working on implementing paging functionality using Paging3, incorporating user-input filtering for item titles. The implementation is generally successful, except for a peculiar scenario:

When the combination of the Flow<PagingData> and the Filter flow occurs, if the first page in the PagingData is filtered out (resulting in 0 elements remaining), the PagingDataAdapter does not request any additional pages. However, if you have already scrolled through several pages and this particular element is in the cache (via .cachedIn(viewModelScope)), the functionality works perfectly.

I am uncertain whether passing an empty PagingData in the initial flow combine misleads the adapter into assuming that endOfPagination has been reached or this is the normal functionality.

First page loaded All Paged Loaded
Only first page has loaded Result after all pages have been loaded

These are my paging classes:

PagingSource

class SchoolPagingSource (
    private var schoolDataBase: SchoolDataBase
) : PagingSource<Int,School>() {

    companion object{
        const val ITEMS_PER_PAGE = 20
    }
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, School> {

        val nextPageNumber = params.key ?: 1
        val nextElementPosition = (nextPageNumber - 1) * ITEMS_PER_PAGE

        return try {
            var response = schoolDataBase.schoolDao().getSchools(ITEMS_PER_PAGE,nextElementPosition)
            
            val key = if (response.size == ITEMS_PER_PAGE)
                nextPageNumber + 1
            else
                null
            
            LoadResult.Page(response, null, key)
        } catch (e: Exception) {
            e.printStackTrace()
            LoadResult.Error(e)
        }
    }
    
    override fun getRefreshKey(state: PagingState<Int, School>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            val anchorPage = state.closestPageToPosition(anchorPosition)
            anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
        }
    }
}

ViewModel

class SchoolsViewModel @Inject constructor(
    private val dataBase: SchoolDataBase,
) : BaseViewModel() {

    private val _filterStringState = MutableStateFlow("")

    //TODO ARREGLAR

    fun getAllSchools(): Flow<PagingData<School>> =
        Pager(config =
        PagingConfig(
            pageSize = ITEMS_PER_PAGE,
            prefetchDistance = 20,
            enablePlaceholders = true
        )) {
            SchoolPagingSource(dataBase)
        }.flow.cachedIn(viewModelScope).combine(_filterStringState) { pagingData, string ->
            pagingData.filter {
                it.getName()
                    .contains(
                        string
                    )
            }
        }.cachedIn(viewModelScope)

    fun filterByString(string: String) = launch { _filterStringState.emit(string) }

}

PagingDataAdapter

class SchoolsPagingDataAdapter() :
    PagingDataAdapter<School, SchoolsPagingDataAdapter.ViewHolder>(SCHOOL_COMPARATOR) {

    companion object{
        private val SCHOOL_COMPARATOR = object : DiffUtil.ItemCallback<School>(){
            override fun areItemsTheSame(oldItem: School, newItem: School): Boolean =
                oldItem.id == newItem.id

            override fun areContentsTheSame(oldItem: School, newItem: School): Boolean =
                oldItem == newItem
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): ViewHolder =
       ViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.item_colegio, parent, false)
       )

    override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(getItem(position))

    inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) {}

I kinda fixed it using adapter.refresh() but is has its own problems

Fragment

schoolFragment.setOnSearchTextWatcher {
            schoolViewModel.filterByString(it)
            if (adapterSchools.snapshot().isEmpty() && !endOfPaginationReached) {
                adapterSchools.refresh()
                endOfPaginationReached = true
            }
        }
adapterSchools.addLoadStateListener { loadState ->
    if (loadState.refresh is LoadState.NotLoading && loadState.append.endOfPaginationReached)
       endOfPaginationReached = true
}
0

There are 0 best solutions below