androidkotlinandroid-jetpack-composeandroid-jetpackandroid-paging

Android Paging 3 library loading infinitely without scroll with Jetpack Compose


I'm attempting to make a paged list of books using Jetpack Compose and Android's Paging 3 library. I am able to make the paged list and get the data fine, but the load() function of my paging data source is being called infinitely, without me scrolling the screen.

My paging data source looks like this:

class GoogleBooksBookSource @Inject constructor(
    private val googleBooksRepository: GoogleBooksRepository,
    private val query: String
): PagingSource<Int, Book>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Book> {
        val position = params.key ?: 0
        return try {
            val response = googleBooksRepository.searchForBookStatic(query, position)
            if (response is Result.Success) {
                LoadResult.Page(
                    data = response.data.items,
                    prevKey = if (position == 0) null else position - 1,
                    nextKey = if (response.data.totalItems == 0) null else position + 1
                )
            } else {
                LoadResult.Error(Exception("Error loading paged data"))
            }
        } catch (e: Exception) {
            Log.e("PagingError", e.message.toString())
            return LoadResult.Error(e)
        }
    }

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

and this is the UI:

Column() {
    // other stuff
    LazyColumn(
        modifier = Modifier.padding(horizontal = 24.dp),
        content = {
            for (i in 0 until searchResults.itemCount) {
                searchResults[i]?.let { book ->
                    item {
                        BookCard(
                            book = book,
                            navigateToBookDetail = { navigateToBookDetail(book.id) }
                        )
                    }
                }
            }
        }
    )
}

As far as I can tell, the data loads correctly and in the correct order, but when I log the API request URLs, it's making infinite calls with an increasing startIndex each time. That would be fine if I was scrolling, since Google Books searches often return thousands of results, but it does this even if I don't scroll the screen.


Solution

  • The issue here was the way I was creating elements in the LazyColumn - it natively supports LazyPagingItem but I wasn't using that. Here is the working version:

    LazyColumn(
        modifier = Modifier.padding(horizontal = 24.dp),
        state = listState,
        content = {
            items(pagedSearchResults) { book ->
                book?.let {
                    BookCard(
                        book = book,
                        navigateToBookDetail = { navigateToBookDetail(book.id) }
                    )
                }
            }
        }
    )