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