androidandroid-jetpack-composeandroid-pagingandroid-paging-3

Jetpack Compose Paging - using key and contentType


So in my app, I am using Compose Paging and I've encountered a problem I am not sure how to approach correctly. Let me describe the issue:

In my paging list, I am displaying two types of items: headers and actual data cells. I am getting data for those data cells from API and I insert headers in between them using the insertSeparators method.

I differentiate the header and cell data object by using a sealed class:

sealed class PagingModel {
    data class Header(@StringRes val label: Int) : PagingModel()
    data class CellData(val data: DataResponse) : PagingModel()
} 

So that in the paging source, I map the api results to PagingModel.CellData and in the insertSeparators, I add the PagingModel.Header with some before/after logic.

Now, to the issue. I want the paging list to be as performant as it can be, so I want to use the key and contentType params in the LazyColumn. Here's a code snippet:

items(
    count = lazyPagingItems.itemCount,
    key = lazyPagingItems.itemKey { **What do I put here when PagingData.Header has no key** },
    contentType = lazyPagingItems.itemContentType {**should it just be "it" ?** }
  ) { index -> ...}

I added the questions about the key and contentType into those lambda brackets. I am mostly interested in the key parameter, i tried something like this:

key = lazyPagingItems.itemKey { if (it is PagingData.CellData) it.id else ???}

But I dont know what to put in the else block, when I omitted it, it throws an expected error, that I can't use Unit as a key.

Is this even doable? Any help will be much appreciated. Thanks!


Solution

  • Yeah, that's definitely doable.
    For the key, you could use the label if you only have unique labels. For it to not collide with cell ids by chance, you can prefix it with "header" or something:

    key = lazyPagingItems.itemKey {
        when (it) {
            is Header -> "header_${it.label}"
            is CellData -> "cell_${it.id}"
        }
    }
    

    If you have multiple headers with the same label, you can create some id for them and use that as a key:

    data class Header(
        @StringRes val label: Int,
        val id: String = UUID.randomUUID().toString(),
    ) : PagingModel()
    

    For the contentType, it can be anything. The important thing is that content types of items that have similar layouts are equal, and content types of items that have different layout are not equal.
    In your case, you have only two layout types, so you can simply do something like this:

    contentType = lazyPagingItems.itemContentType { it is Header }
    

    with that, Header has contentType == true and Cell has contentType == false. That works. If you have more then two types, you can use integers. Or, if you want something more fancy, you can create an enum:

    enum class MyListContentType { Header, Cell, ... }
    
    contentType = lazyPagingItems.itemContentType {
        when (it) {
            is Header -> MyListContentType.Header
            is CellData -> MyListContentType.Cell
        }
    }