'Paging3 with ViewPager not working (where pages use the same type of PagingSource)

I'm trying to use Paging3 with ViewPager in a project, but something strange is happening. I think it is due to the fact that each page uses the same type of PagingSource.

Here's how the page is structured:

  • Main Fragment
    • View Pager
      • Cars Fragment 1 (red)
        • Uses Cars ViewModel (not shared)
        • Fetches red cars from the API (a new LiveData<PagingData<Cars>> is returned)
      • Cars Fragment 2 (green)
        • Uses Cars ViewModel (not shared)
        • Fetches green cars from the API (a new LiveData<PagingData<Cars>> is returned)
      • Cars Fragment 3 (blue)
        • Uses Cars ViewModel (not shared)
        • Fetches blue cars from the API (a new LiveData<PagingData<Cars>> is returned)

Suppose there are 20 red cards, 40 blue cars and 80 green cars in the api database. If I open the red cars page first and scroll all the way down (till all red cars are loaded) and then select the green or blue cars page, no additional cars are loaded (even though there are more to load) when I scroll down those pages.

That's how I'm fetching the cars in the ViewModel:

val carsPagingData = carsFilter.switchMap { filter ->
    repository.fetchCars(filter = filter).cachedIn(viewModelScope)
}

Is there a problem with Paging3 + ViewPager when we use the same type of PagingSource in different pages? If so, is there any way I can make it work properly?


CarsPagingSource:

class CarsPagingSource(
    private val carsService: CarsService,
    private val filter: CarsFilter
) : PagingSource<Int, CarDto>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, CarDto> {
        val position = params.key ?: CARS_STARTING_PAGE_INDEX
        return try {
            val response = carsService.fetchCars(filter = filter.copy(pageNumber = position))
            val cars = response.body()?.cars

            if (response.isSuccessful && cars != null) {
                LoadResult.Page(
                    data = cars,
                    prevKey = if (position == CARS_STARTING_PAGE_INDEX) {
                        null
                    } else {
                        position - 1
                    },
                    nextKey = if (cars.isEmpty()) {
                        null
                    } else {
                        position + 1
                    }
                )
            } else {
                LoadResult.Error(RequestNotSuccessfulException(response.code(), emptyList()))
            }
        } catch (exception: IOException) {
            return LoadResult.Error(exception)
        } catch (exception: HttpException) {
            return LoadResult.Error(exception)
        }
    }

    override fun getRefreshKey(state: PagingState<Int, CarDto>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
                ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
        }
    }

    companion object {
        private const val CARS_STARTING_PAGE_INDEX = 1
    }
}


Solution 1:[1]

As anyways you are using switch map on filter var. It's better if you use shared ViewModel across Main fragment and all 3 cars fragment.

viewmodel instantiation like,

val viewModel: CarsViewModel by activityViewModels { CustomFactory() /* If needed */ }

and in each fragment while in onResume(),

if (isResumed) {
   viewModel.loadCars(filter) // Filter to distinguish fragment calls
   pagingAdapter.refresh()
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Willey Hute