Annon
Annon

Reputation: 843

LazyVerticalGrid for paging items in jetpack compose

I'm currently using paging items (LazyPagingItems) in LazyVerticalGrid via below extention function and it works fine.

inline fun <T : Any> LazyGridScope.items(
    items: LazyPagingItems<T>,
    crossinline itemContent: @Composable LazyGridItemScope.(item: T?) -> Unit
) {
    items(count = items.itemCount) { index ->
        itemContent(items[index])
    }
}

However, i want to make use of other parameters that LazyLazyout provides like key, span etc and tried below function.

inline fun <T : Any> LazyGridScope.items(
    items: LazyPagingItems<T>,
    noinline key: ((item: T?) -> Any)? = null,
    noinline span: (LazyGridItemSpanScope.(item: T?) -> GridItemSpan)? = null,
    noinline contentType: (item: T?) -> Any? = { null },
    crossinline itemContent: @Composable LazyGridItemScope.(item: T?) -> Unit
) = items(
    count = items.itemCount,
    key = if (key != null) { index: Int -> key(items[index]) } else null,
    span = if (span != null) { { span(items[it]) } } else null,
    contentType = { index: Int -> contentType(items[index]) }
) {
    itemContent(items[it])
}

When i use that function with key, it shows errors that Type mismatch: inferred type is Long? but Any was expected.

items(items = products, key = { product -> product?.productId }) {
//Content
}

I guess this is due to declaration of public class LazyPagingItems<T : Any>. How do I resolve it to use all parameters with paging items ?

Upvotes: 3

Views: 2827

Answers (2)

Sebastian
Sebastian

Reputation: 806

I struggle with a "Key" problem since I updated from androidx.compose "1.3.0-beta03" to "1.3.0-rc01".

I use LazyPagingItems for a LazyVerticalGrid and i add items like this in the LazyGridScope:

(pagingData: Flow<PagingData<DangerItem>>){
val feedData = pagingData.collectAsLazyPagingItems()
items(feedData.itemCount,
        // to maximize the performance add a unique key
        // as fallback use index of element
        key = { index ->
            feedData[index]?.code ?: index
        },
        contentType = { idx ->
            if (feedData[idx] != null) 1 else 0
        }
    )

But since the library update, when I filter the content (so the paging items are updated in the flow), it throws an Exception. This exception wasn't thrown in the beta03 library version.

java.lang.IllegalArgumentException: Failed requirement. 
at androidx.compose.foundation.lazy.grid.LazyGridSpanLayoutProvider.getLineIndexOfItem--_Ze7BM(LazyGridSpanLayoutProvider.kt:174) 
at androidx.compose.foundation.lazy.grid.LazyGridItemPlacementAnimatorKt.lastIndexInPreviousLineBefore(LazyGridItemPlacementAnimator.kt:489)
at androidx.compose.foundation.lazy.grid.LazyGridItemPlacementAnimatorKt.access$lastIndexInPreviousLineBefore(LazyGridItemPlacementAnimator.kt:1)

Because paging data is changing and elements become null while loading I can't supply a unique key, the App crashes. Is this now a bug in the library or what exactly is the best practice when using LazyPagingItems that can change over time.

enter image description here

I found this workaround.

key = { idx ->
                if (feedData[feedData.itemCount - 1] == null || feedData.itemCount <= 1) idx
                else feedData[idx]?.code ?: idx
            },

Upvotes: 0

Phil Dukhov
Phil Dukhov

Reputation: 87605

With product?.productId you are trying to pass an optional Int? value, while key expects a non-optional Any value.

When you pass key = null to the computation block, under the hood it creates a unique key for each object depending on the index. A particular item key cannot be null as that would make it equal to other item keys, which is forbidden.

You can make call key only for non optional items, and provide index as the default value for optional case:

inline fun <T : Any> LazyGridScope.items(
    // ...
    noinline key: ((item: T) -> Any)? = null,
    // ...
) = items(
    // ...
    key = if (key != null) { index: Int -> items[index]?.let(key) ?: index } else null,
    // ...

Usage:

items(items = products, key = { product -> product.productId }) {

Upvotes: 1

Related Questions