Jetpack Compose: IndexOutOfBoundsException in LazyColumn with Paging 3

600 views Asked by At

I have a simple LazyColumn showing a list of paginated data coming from Room.

The usage of the LazyColumn with paging 3 is inspired from the official docs (https://developer.android.com/reference/kotlin/androidx/paging/compose/LazyPagingItems)

@Composable
fun <T : Any> PaginatedLazyColumn(
    items: LazyPagingItems<T>,
    pageLoadContent: @Composable (LazyItemScope.() -> Unit),
    pageErrorContent: @Composable (LazyItemScope.(throwable: Throwable) -> Unit),
    modifier: Modifier = Modifier,
    state: LazyListState = rememberLazyListState(),
    itemContent: @Composable (LazyItemScope.(item: T, index: Int) -> Unit),
) {
    LazyColumn(
        modifier = modifier,
        state = state,
    ) {
        when (val loadState = items.loadState.prepend) {
            is LoadState.NotLoading -> Unit
            is LoadState.Loading -> item { pageLoadContent() }

            is LoadState.Error -> item { pageErrorContent(loadState.error) }
        }

        items(
            count = items.itemCount,
        ) { index ->
            val item = items[index]
            item?.let { itemContent(it, index) }
        }

        when (val loadState = items.loadState.append) {
            is LoadState.NotLoading -> Unit
            is LoadState.Loading -> item { pageLoadContent() }

            is LoadState.Error -> item { pageErrorContent(loadState.error) }
        }
    }
}

Data is exposed through the ViewModel like this:

val items: Flow<PagingData<SubscriptionItem>> = filterParams.flatMapLatest {
        interactor.getList(
            it.query.nullifyBlank(), it.filter.getVideoType(),
            PagingConfig(pageSize = PagingUtils.LIST_PAGE_SIZE)
        )
    }.cachedIn(viewModelScope)

And the interactor returns the repository data which is a Pager

override fun getList(
        query: String?, videoType: VideoType?,
        tmdbPosterPathFormat: String, languageId: Long,
        config: PagingConfig
    ): Flow<PagingData<SubscriptionItem>> {
        val pagingSourceFactory = {
            subscriptionDao.getList(query.like(), videoType, tmdbPosterPathFormat, languageId)
        }
        return Pager(
            config = config,
            pagingSourceFactory = pagingSourceFactory
        ).flow.map { pagingData ->
            pagingData.map { item ->
                when (item.type) {
                    VideoType.Series -> SubscriptionItem.Series(
                        item.id, tmdbId = item.tmdbId, name = item.name,
                        imageUrl = item.image, year = item.releaseYear, status = SeriesStatus.parse(item.status)
                    )

                    VideoType.Movie -> SubscriptionItem.Movie(
                        item.id, tmdbId = item.tmdbId, title = item.name,
                        imageUrl = item.image, year = item.releaseYear, status = MovieStatus.parse(item.status)
                    )
                }
            }
        }
    }

When I first open the screen containing the LazyColumn the items are shown correctly, but when I filter the list by a search I get the following exception:

java.lang.IndexOutOfBoundsException: Index 1, size 1
    at androidx.compose.foundation.lazy.layout.MutableIntervalList.checkIndexBounds(IntervalList.kt:183)
    at androidx.compose.foundation.lazy.layout.MutableIntervalList.get(IntervalList.kt:166)
    at androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent.getKey(LazyLayoutIntervalContent.kt:86)
    at androidx.compose.foundation.lazy.LazyListItemProviderImpl.getKey(LazyListItemProvider.kt:85)
    at androidx.compose.foundation.lazy.LazyListMeasuredItemProvider.getAndMeasure(LazyListMeasuredItemProvider.kt:46)
    at androidx.compose.foundation.lazy.LazyListMeasureKt.createItemsAfterList(LazyListMeasure.kt:418)
    at androidx.compose.foundation.lazy.LazyListMeasureKt.measureLazyList-50ZNEM8(LazyListMeasure.kt:278)
    at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke-0kLqBqw(LazyList.kt:328)
    at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke(LazyList.kt:193)
    at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke-0kLqBqw(LazyLayout.kt:107)
    at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke(LazyLayout.kt:100)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:708)
    at androidx.compose.ui.node.InnerNodeCoordinator$LookaheadDelegateImpl.measure-BRTryo0(InnerNodeCoordinator.kt:81)
    at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:646)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator$LookaheadDelegateForLayoutModifierNode.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:64)
    at androidx.compose.foundation.layout.FillNode.measure-3p2s80s(Size.kt:699)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator$LookaheadDelegateForLayoutModifierNode.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:64)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performLookaheadMeasure$1.invoke(LayoutNodeLayoutDelegate.kt:1622)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performLookaheadMeasure$1.invoke(LayoutNodeLayoutDelegate.kt:1621)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2303)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:496)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:256)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:111)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release$default(OwnerSnapshotObserver.kt:105)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performLookaheadMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1621)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performLookaheadMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:36)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$LookaheadPassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1289)
    at androidx.compose.ui.node.LayoutNode.lookaheadRemeasure-_Sx5XlM$ui_release(LayoutNode.kt:1126)
    at androidx.compose.ui.node.LayoutNode.lookaheadRemeasure-_Sx5XlM$ui_release$default(LayoutNode.kt:1120)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.doLookaheadRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:313)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:478)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded$default(MeasureAndLayoutDelegate.kt:461)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:354)
    at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:969)
    at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.kt:230)
    at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1222)
    at android.view.View.draw(View.java:22576)
    at android.view.View.updateDisplayListIfDirty(View.java:21434)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4535)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4508)
    at android.view.View.updateDisplayListIfDirty(View.java:21392)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4535)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4508)
    at android.view.View.updateDisplayListIfDirty(View.java:21392)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4535)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4508)
    at android.view.View.updateDisplayListIfDirty(View.java:21392)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4535)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4508)
    at android.view.View.updateDisplayListIfDirty(View.java:21392)
    at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:559)
    at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:567)
    at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:651)
    at android.view.ViewRootImpl.draw(ViewRootImpl.java:4316)
    at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4017)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3278)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2078)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8553)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1142)
    at android.view.Choreographer.doCallbacks(Choreographer.java:962)
    at android.view.Choreographer.doFrame(Choreographer.java:887)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1127)
    at android.os.Handler.handleCallback(Handler.java:938)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:237)
    at android.app.ActivityThread.main(ActivityThread.java:8090)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:656)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967)

Seems something similar to this: Application crash due to IndexOutOfBoundsException in Lazycolumn Jetpack compose

Am I doing something wrong? Anyone is having the same issue?

I'm using:

  • Room 2.6.0
  • Compose 2023.10.01
  • Paging 3.2.1

Thanks in advance

0

There are 0 answers