androidkotlinandroid-jetpack-composelazycolumn

How to fix crash with "Vertically scrollable component was measured with an infinity maximum height constraints"?


When I'm trying to use Lazy elements with my data list application are crashing. This crash has next scenario:

  1. Calling NavHost for screen with Lazy provokes little freez and restart app.
  2. After restart app when call NavHost for the same screen provoke crash with system message "app keeps stopping" in Log no any fatal error

I tried different ways for sending my data to lazy. And with Log.d have seen that data are available before items(){} function should start.

Also I tried to send any random simple data to build Lazy localy in Composable (e.g list of 1..10, and text elements with values)

Everythind works when I'm usind simple Column, Rows, or 1 element

There are my current code for this element. But it also dont work if I use Any Text() instead of Content element.

@Composable
fun ContentGrid(
    content: List<Item>,
) {
    LazyColumn {
        items(content) {
            ContentElement(item = it)
        }
    }
}

@Composable
fun ContentElement(item: Item) {
    Card(
        modifier = Modifier
            .fillMaxWidth(0.4f)
            .fillMaxHeight(0.3f)
            .clip(RoundedCornerShape(10))
            .clickable(enabled = true, onClick = { }),
        colors = CardDefaults.cardColors(
            containerColor = Color(200, 200, 238, 226),
            contentColor = Color.Black,
        )
    ) {
        Column(
            modifier = Modifier.padding(5.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.SpaceBetween,
        ) {
            Image(
                painter = rememberAsyncImagePainter(item.url),
                contentDescription = null,
                contentScale = ContentScale.FillWidth,
                modifier = Modifier
                    .size(100.dp),
            )
            /* AsyncImage(
                  model = item.url,
                  contentDescription = item.title,
                  modifier = Modifier
                      .size(200.dp, 200.dp)
                      .clip(RoundedCornerShape(10)),
                  contentScale = ContentScale.FillWidth),*/
            Column(
                Modifier.fillMaxWidth(),
                horizontalAlignment = Alignment.Start,
            ) {
                Spacer(Modifier.height(10.dp))
                Text(item.title, fontSize = 15.sp, fontWeight = FontWeight.Bold)
                Spacer(Modifier.height(10.dp))
                Text(item.price, fontSize = 20.sp, fontWeight = FontWeight.Bold)
            }
        }
    }
}

This is the exception that leads to the crash:

java.lang.IllegalStateException: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed. One of the common reasons is nesting layouts like LazyColumn and Column(Modifier.verticalScroll()). If you want to add a header before the list of items please add a header as a separate item() before the main items() inside the LazyColumn scope. There are could be other reasons for this to happen: your ComposeView was added into a LinearLayout with some weight, you applied Modifier.wrapContentSize(unbounded = true) or wrote a custom layout. Please try to remove the source of infinite constraints in the hierarchy above the scrolling container.
    at androidx.compose.foundation.CheckScrollableContainerConstraintsKt.checkScrollableContainerConstraints-K40F9xA(CheckScrollableContainerConstraints.kt:35)
    at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke-0kLqBqw(LazyList.kt:187)
    at androidx.compose.foundation.lazy.LazyListKt$rememberLazyListMeasurePolicy$1$1.invoke(LazyList.kt:183)
    at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke-0kLqBqw(LazyLayout.kt:119)
    at androidx.compose.foundation.lazy.layout.LazyLayoutKt$LazyLayout$3$2$1.invoke(LazyLayout.kt:112)
    at androidx.compose.ui.layout.LayoutNodeSubcompositionsState$createMeasurePolicy$1.measure-3p2s80s(SubcomposeLayout.kt:725)
    at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:135)
    at androidx.compose.ui.graphics.SimpleGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:646)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:188)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:316)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:315)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:503)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1775)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:40)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:696)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:672)
    at androidx.compose.animation.AnimatedEnterExitMeasurePolicy.measure-3p2s80s(AnimatedVisibility.kt:812)
    at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:135)
    at androidx.compose.animation.EnterExitTransitionModifierNode.measure-3p2s80s(EnterExitTransition.kt:1173)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:188)
    at androidx.compose.ui.graphics.BlockGraphicsLayerModifier.measure-3p2s80s(GraphicsLayerModifier.kt:578)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:188)
    at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1$1$1.invoke-3p2s80s(AnimatedContent.kt:781)
    at androidx.compose.animation.AnimatedContentKt$AnimatedContent$6$1$1$1.invoke(AnimatedContent.kt:780)
    at androidx.compose.ui.layout.LayoutModifierImpl.measure-3p2s80s(LayoutModifier.kt:294)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:188)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:316)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:315)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:503)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1775)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:40)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:696)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:672)
    at androidx.compose.animation.AnimatedContentMeasurePolicy.measure-3p2s80s(AnimatedContent.kt:837)
    at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:135)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:316)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:315)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:2441)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:502)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:258)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1775)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:40)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:696)
    at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release(LayoutNode.kt:1221)
    at androidx.compose.ui.node.LayoutNode.remeasure-_Sx5XlM$ui_release$default(LayoutNode.kt:1212)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure-sdFAvZA(MeasureAndLayoutDelegate.kt:369)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:566)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded$default(MeasureAndLayoutDelegate.kt:534)
    at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:390)
    at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:1273)
    at androidx.compose.ui.node.Owner.measureAndLayout$default(Owner.kt:248)
    at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:1555)
    at android.view.View.draw(View.java:24845)
    at android.view.View.updateDisplayListIfDirty(View.java:23692)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4521)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4494)
    at android.view.View.updateDisplayListIfDirty(View.java:23646)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4521)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4494)
    at android.view.View.updateDisplayListIfDirty(View.java:23646)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4521)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4494)
    at android.view.View.updateDisplayListIfDirty(View.java:23646)
    at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4521)
    at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4494)
    at android.view.View.updateDisplayListIfDirty(View.java:23646)
    at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:694)
    at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:700)
    at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:798)
    at android.view.ViewRootImpl.draw(ViewRootImpl.java:5659)
    at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:5312)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4303)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2917)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:10324)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1406)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1415)
    at android.view.Choreographer.doCallbacks(Choreographer.java:1015)
    at android.view.Choreographer.doFrame(Choreographer.java:945)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1389)
    at android.os.Handler.handleCallback(Handler.java:959)
    at android.os.Handler.dispatchMessage(Handler.java:100)
    at android.os.Looper.loopOnce(Looper.java:232)
    at android.os.Looper.loop(Looper.java:317)
    at android.app.ActivityThread.main(ActivityThread.java:8705)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:580)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:886)

Solution

  • You cannot use fillMaxHeight for your Card when it is in a LazyColumn.

    What would you expect to happen? The LazyColumn provides a container with unlimited (and undetermined) height. And now you want to set each Card's height to a 0.3f fraction of this unlimited height. That cannot possibly work.

    What you can do is to set the Card's height to 0.3f of the visible part of the LazyColumn by using fillParentMaxHeight. The visible part of a LazyColumn has a concrete, finite value.

    Since fillParentMaxHeight is only available inside a LazyItemScope you need to pass it as a parameter:

    LazyColumn {
        items(content) {
            ContentElement(
                item = it,
                modifier = Modifier
                    .fillMaxWidth(0.4f)
                    .fillParentMaxHeight(0.3f)
            )
        }
    }
    
    @Composable
    fun ContentElement(
        item: Item,
        modifier: Modifier = Modifier,
    ) {
        Card(
            modifier = modifier
                .clip(RoundedCornerShape(10))
                .clickable(enabled = true, onClick = { }),
        ) { /*...*/ }
    }