'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)
- Cars Fragment 1 (red)
- View Pager
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 |