Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Load next data when on click of button using the Paging 3 library for Compose

Currently Paging library handles when to load the data automatically when a user scrolls down. But what if you want to give the user full authority for when they want the next page of data to be loaded i.e when button is clicked show next page of movies. How can you handle this in Paging library? See below how I've implemented the paging to load data as a user scrolls down

Here below this how I implemented the Paging to load next page when user scrolls down


class MoviesPagingDataSource(
    private val repo: MoviesRepository,
 ) : PagingSource<Int, Movies>() {

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

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movies> {
        return try {
            val nextPageNumber = params.key ?: 0
            val response = repo.getMovies(page = nextPageNumber, size = 10)
            LoadResult.Page(
                data = response.content,
                prevKey = null,
                nextKey = if (response.content.isNotEmpty()) response.number + 1 else null
            )
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
 }

This is how I emit the state in ViewModel for the UI to observe


@HiltViewModel
 class MoviesViewModel @Inject constructor(
   private val moviesRepository: MoviesRepository
): ViewModel {
   ....
   //emitting the data to the UI to observe

        val moviesPagingDataSource = Pager(PagingConfig(pageSize = 10)) {
        MoviesPagingDataSource(moviesRepository)
       }.flow.cachedIn(viewModelScope)
}

How I'm observing it in the UI


@Composable
fun MoviesList(viewModel: MoviesViewModel) {

    val moviesList = viewModel.moviesPagingDataSource.collectAsLazyPagingItems()

    LazyColumn {
        items(moviesList) { item ->
            item?.let { MoviesCard(movie = it) }
        }

        when (moviesList.loadState.append) {
            is LoadState.NotLoading -> Unit
            LoadState.Loading -> {
                item {
                    LoadingItem()
                }
            }
            is LoadState.Error -> {
                item {
                    ErrorItem(message = "Some error occurred")
                }
            }
        }

        when (moviesList.loadState.refresh) {
            is LoadState.NotLoading -> Unit
            LoadState.Loading -> {
                item {
                    Box(
                        modifier = Modifier.fillMaxSize(),
                        contentAlignment = Center
                    ) {
                        CircularProgressIndicator()
                    }
                }
            }
            is LoadState.Error -> TODO()
        }
    }
}

So currently I'm adding 1 to the previous page every time a user clicks the button to load more movies then saving this movies to the list state. Also making sure the current page is greater than or equal to total pages before loading more data and adding to the list state of previous loaded movies

like image 436
Breens Mbaka Avatar asked Sep 06 '25 17:09

Breens Mbaka


1 Answers

you can use Channel to block LoadResult returning from load, when user clicks the button, send an element to the Channel. here is the simple: https://github.com/sunchao0108/ComposePagingDemo

like image 90
Jack Avatar answered Sep 11 '25 01:09

Jack