Issue appears on multiple screens. To my surprise the same code was working for previous versions of app. I just updated the compose libraries and then all this happening again and again on multiple locations.
I am using LazyColumn to populate list items. Whenever I try to scroll it, the App crashes. Below is the abstract code, that I’m using. The issue appears after updating compose Bom version to 2023-08-00. The same code works for other screens. The only difference here is that the lazy column item is almost half of the screen height(1:1 aspect ratio). So mostly two items per screen.
val users = viewModel.usersList
val lazyColumnListState = rememberLazyListState()
val shouldStartPaginate = remember {
derivedStateOf {
viewModel.canPaginate && (lazyColumnListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
?: -9) >= (lazyColumnListState.layoutInfo.totalItemsCount - 9)
}
}
LaunchedEffect(key1 = shouldStartPaginate.value) {
if (shouldStartPaginate.value && viewModel.listState == ListState.IDLE)
viewModel.getMoreUsers()
}
//Inside Scafold
LazyColumn(
state = lazyColumnListState,
verticalArrangement = Arrangement.spacedBy(12.dp),
modifier = Modifier.padding(top = 16.dp)
) {
items(
items = users,
key = { it.userID ?: (1000..5000).random() }) { i ->
ComposeExploreItem(i)
}
if (users.isNotEmpty()) {
item {
Spacer(modifier = Modifier.height(40.dp))
}
}
composeLazyColumnPagingLoader(
viewModel.listState,
users,
"No users available"
)
}
Here is the Crash Log.
java.lang.IllegalStateException: replace() called on item that was not placed
at androidx.compose.ui.node.LayoutNodeLayoutDelegate$LookaheadPassDelegate.replace(LayoutNodeLayoutDelegate.kt:1517)
at androidx.compose.ui.node.LayoutNode.lookaheadReplace$ui_release(LayoutNode.kt:926)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:471)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.access$remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:39)
at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:352)
at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:908)
at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.kt:228)
at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1156)
at android.view.View.draw(View.java:23269)
at android.view.View.updateDisplayListIfDirty(View.java:22133)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4513)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4486)
at android.view.View.updateDisplayListIfDirty(View.java:22089)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4513)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4486)
at android.view.View.updateDisplayListIfDirty(View.java:22089)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4513)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4486)
at android.view.View.updateDisplayListIfDirty(View.java:22089)
at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4513)
at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4486)
at android.view.View.updateDisplayListIfDirty(View.java:22089)
at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:689)
at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:695)
at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:793)
at android.view.ViewRootImpl.draw(ViewRootImpl.java:4789)
at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4500)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3687)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2371)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:9297)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1231)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
at android.view.Choreographer.doCallbacks(Choreographer.java:899)
at android.view.Choreographer.doFrame(Choreographer.java:832)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7918)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
2
Answers
While my solution isn’t relevant in the case of
LazyColumn
, the bug happens due to some already drawn composable being redrawn.In my case it happened due to me using
HorizontalPager
as in XML layoutsViewPager
with very complex screen inside – so debugging became hell.It can be overcome by adding
beyondBoundsPageCount
with a value to yourHorizontalPager
because default value is0
!I’m not sure whether it’s an issue of Google Compose team or me doing something wrong but I’m sure the fix isn’t as straight forward as just updating a library (for me at least, I tried upgrading and downgrading multiple times and multiple possible libraries)
While I do still not know the source of issue, I’ve found way to avoid the crash by wrapping your scrollable layout into
ConstraintLayout
such as
ConstraintLayout(modifier = Modifier.fillMaxSize()) { LazyColumn(Modifier.constraintAs(...) {...}) {...} }
Hope it helps.