constraintlayout icon indicating copy to clipboard operation
constraintlayout copied to clipboard

MotionLayout is crashing

Open robertlevonyan opened this issue 3 years ago • 6 comments

The motion layout is crashing with this error.

java.lang.IllegalArgumentException: Failed requirement.
        at androidx.compose.ui.node.MeasureAndLayoutDelegate.doRemeasure(MeasureAndLayoutDelegate.kt:177)
        at androidx.compose.ui.node.MeasureAndLayoutDelegate.remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:228)
        at androidx.compose.ui.node.MeasureAndLayoutDelegate.access$remeasureAndRelayoutIfNeeded(MeasureAndLayoutDelegate.kt:38)
        at androidx.compose.ui.node.MeasureAndLayoutDelegate.measureAndLayout(MeasureAndLayoutDelegate.kt:201)
        at androidx.compose.ui.platform.AndroidComposeView.measureAndLayout(AndroidComposeView.android.kt:662)
        at androidx.compose.ui.node.Owner$DefaultImpls.measureAndLayout$default(Owner.kt:182)
        at androidx.compose.ui.platform.AndroidComposeView.dispatchDraw(AndroidComposeView.android.kt:846)
        at android.view.View.draw(View.java:22648)
        at android.view.View.updateDisplayListIfDirty(View.java:21520)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4512)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4485)
        at android.view.View.updateDisplayListIfDirty(View.java:21476)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4512)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4485)
        at android.view.View.updateDisplayListIfDirty(View.java:21476)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4512)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4485)
        at android.view.View.updateDisplayListIfDirty(View.java:21476)
        at android.view.ViewGroup.recreateChildDisplayList(ViewGroup.java:4512)
        at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:4485)
        at android.view.View.updateDisplayListIfDirty(View.java:21476)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:534)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:540)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:616)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:4438)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:4166)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3326)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2143)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8665)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1037)
        at android.view.Choreographer.doCallbacks(Choreographer.java:845)
        at android.view.Choreographer.doFrame(Choreographer.java:780)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1022)
        at android.os.Handler.handleCallback(Handler.java:938)
        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:7839)
        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:1003)

this is my code

  var sectionsState: Boolean by rememberSaveable { mutableStateOf(false) }

  val collapsedSet = getCollapsedConstraints()
  val expandedSet = getExpandedConstraints()

  val progress by animateFloatAsState(
    targetValue = if (sectionsState) 1f else 0f,
    animationSpec = tween(150)
  )

MotionLayout(
      start = collapsedSet,
      end = expandedSet,
      progress = progress,
      modifier = Modifier.fillMaxSize(),
    ) {
...
}



private fun getCollapsedConstraints(): ConstraintSet = ConstraintSet {
  val title = createRefFor("title")
  val subtitle = createRefFor("subtitle")
  val toggle = createRefFor("toggle")
  val sections = createRefFor("sections")
  val timers = createRefFor("timers")


  constrain(title) {
    width = Dimension.wrapContent
    height = Dimension.wrapContent
    end.linkTo(parent.end)
    start.linkTo(parent.start)
    top.linkTo(parent.top)
  }
  constrain(subtitle) {
    width = Dimension.wrapContent
    height = Dimension.wrapContent
    end.linkTo(parent.end)
    start.linkTo(parent.start)
    top.linkTo(title.bottom)
  }
  constrain(sections) {
    width = Dimension.matchParent
    height = Dimension.wrapContent
    start.linkTo(parent.start)
    end.linkTo(parent.end)
    bottom.linkTo(parent.top)
  }
  constrain(toggle) {
    end.linkTo(parent.end)
    top.linkTo(sections.bottom)
  }
  constrain(timers) {
    width = Dimension.wrapContent
    height = Dimension.wrapContent
    start.linkTo(parent.start)
    end.linkTo(parent.end)
    bottom.linkTo(parent.bottom)
  }
}

private fun getExpandedConstraints(): ConstraintSet = ConstraintSet {
  val title = createRefFor("title")
  val subtitle = createRefFor("subtitle")
  val toggle = createRefFor("toggle")
  val sections = createRefFor("sections")
  val timers = createRefFor("timers")


  constrain(title) {
    width = Dimension.wrapContent
    height = Dimension.wrapContent
    end.linkTo(parent.end)
    start.linkTo(parent.start)
    top.linkTo(parent.top)
  }
  constrain(subtitle) {
    width = Dimension.wrapContent
    height = Dimension.wrapContent
    end.linkTo(parent.end)
    start.linkTo(parent.start)
    top.linkTo(title.bottom)
  }
  constrain(sections) {
    width = Dimension.matchParent
    height = Dimension.wrapContent
    start.linkTo(parent.start)
    end.linkTo(parent.end)
    top.linkTo(parent.top)
  }
  constrain(toggle) {
    end.linkTo(parent.end)
    top.linkTo(sections.bottom)
  }
  constrain(timers) {
    width = Dimension.wrapContent
    height = Dimension.wrapContent
    start.linkTo(parent.start)
    end.linkTo(parent.end)
    bottom.linkTo(parent.bottom)
  }
}

robertlevonyan avatar Feb 25 '22 12:02 robertlevonyan

Which Compose version is this?

Haven't been able to repro on 1.0.0 or even 1.1.0.

I'm using a very simple setup:

@OptIn(ExperimentalMotionApi::class)
@Preview
@Composable
fun CrashTest() {
    var sectionsState: Boolean by rememberSaveable { mutableStateOf(false) }

    val collapsedSet = getCollapsedConstraints()
    val expandedSet = getExpandedConstraints()

    val progress by animateFloatAsState(
        targetValue = if (sectionsState) 1f else 0f,
        animationSpec = tween(150)
    )

    Column {
        MotionLayout(
            start = collapsedSet,
            end = expandedSet,
            progress = progress,
            modifier = Modifier.fillMaxWidth().weight(1.0f, true),
        ) {
            Text(
                text = "My Title",
                Modifier
                    .layoutId("title")
                    .background(Color.Red))
            Text(
                text = "My subtitle",
                Modifier
                    .layoutId("subtitle")
                    .background(Color.Blue))
            Text(
                text = "TOGGLE",
                Modifier
                    .layoutId("toggle")
                    .background(Color.Cyan))
            Text(
                text = "Sections",
                Modifier
                    .layoutId("sections")
                    .background(Color.Yellow))
            Text(
                text = "Timers:",
                Modifier
                    .layoutId("timers")
                    .background(Color.Gray))
        }
        Button(onClick = { sectionsState = !sectionsState }) {
            Text(text = "Run")
        }
    }
}

oscar-ad avatar Mar 16 '22 21:03 oscar-ad

Any chance one of the Composables in MotionLayout has a size Modifier? (size, sizeIn, defaultMin, fillMaxSize, etc)

Does it still crash with Dimension.fillToConstraints instead of Dimension.matchParent?

oscar-ad avatar Mar 16 '22 21:03 oscar-ad

Here is an example that shows the same error message, https://github.com/jipariz/ComposeMotion from the article, https://www.strv.com/blog/collapsing-toolbar-using-jetpack-compose-motion-layout-engineering.

Based on this ComposeMotion project, update the followings:

  1. compose_version = '1.1.1'
  2. classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
  3. distributionUrl=https://services.gradle.org/distributions/gradle-7.3-bin.zip

Then compile and run. The exact same code works on older Compose versions, 1.0.1 and 1.04. However, it doesn't work with 1.1.1 with the error message. Thanks.

Eric-Cen avatar Apr 10 '22 04:04 Eric-Cen

Also had this issue today using the repo @Eric-Cen linked. @oscar-ad do you know if there's any workaround for this?

smithc42 avatar Apr 24 '22 16:04 smithc42

No workaround other than sticking to 1.0.x

It's a hard check that prevents us from remeasuring Composables during animation in some cases.

I'll close this once we work out a solution with the rest of the Compose team.

oscar-ad avatar Apr 25 '22 16:04 oscar-ad

Issue seems to be resolved when using 1.2.0-alpha08 Tho I see issues with custom properties.

I'll keep this open until 1.2.0 hits stable or I'm able to guarantee no regressions from there.

oscar-ad avatar Apr 28 '22 00:04 oscar-ad