MotionLayout is crashing
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)
}
}
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")
}
}
}
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?
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:
- compose_version = '1.1.1'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
- 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.
Also had this issue today using the repo @Eric-Cen linked. @oscar-ad do you know if there's any workaround for this?
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.
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.