Jetpack Compose 1.1.1:LaunchedEffect中布尔值变更未触发重组
问题描述
尝试使用MotionLayout与AnimatedVisibility实现带动画过渡的界面,逻辑如下:
- 在LaunchedEffect中,当
animationProgress(浮点值)小于1时,每10毫秒增加0.05f; - 当
animationProgress≥1f时,将布尔值showSummary设为true。
但showSummary为true时,对应的内容并未显示。不过添加Text组件展示animationProgress和showSummary的字符串值时,二者的变化能正常在界面上呈现。
原代码:
@OptIn(ExperimentalMotionApi::class) @Composable fun SomeView() { var showSummary by remember { mutableStateOf(false) } var animationProgress by remember { mutableStateOf(0f) } LaunchedEffect(key1 = animationProgress, key2 = showSummary) { if (animationProgress < 1f) { delay(10L) animationProgress += 0.05f } if (animationProgress >= 1f) showSummary = true } MotionLayout( start = ConstraintSet("""{ checkmark: { width: 100, height: 100, start: ['outerParent', 'start', 0], end: ['outerParent', 'end', 0], top: ['outerParent', 'top', 0], bottom: ['outerParent', 'bottom', 0] } }""".trimMargin()), end = ConstraintSet("""{ checkmark: { width: 100, height: 100, top: ['outerParent', 'top', 0], start: ['outerParent', 'start', 0], end: ['outerParent', 'end', 0], }, body: { width: 'spread', top: ['checkmark', 'bottom', 16], start: ['outerParent', 'start', 0], end: ['outerParent', 'end', 0] } }""".trimMargin() ), progress = animationProgress ) { Column( modifier = Modifier .fillMaxSize() .padding(bottom = 40.dp) .layoutId("outerParent") ) { Checkmark() AnimatedVisibility ( visible = showSummary, enter = fadeIn(), exit = fadeOut(), modifier = Modifier.fillMaxSize() ) { Column( modifier = Modifier .layoutId("body") .fillMaxWidth() ) { Text(text = "Header") Text(text = "Content") } } } } }
问题根源
AnimatedVisibility嵌套在outerParent的Column中,MotionLayout无法识别body的布局ID——因为MotionLayout只会解析直接子元素的layoutId。- LaunchedEffect的key设置为
animationProgress和showSummary,会导致动画逻辑被重复触发,可能引发状态更新异常。
修复方案
修改后的代码:
@OptIn(ExperimentalMotionApi::class) @Composable fun SomeView() { var showSummary by remember { mutableStateOf(false) } var animationProgress by remember { mutableStateOf(0f) } // 用Unit作为key,确保动画逻辑只执行一次 LaunchedEffect(Unit) { while (animationProgress < 1f) { delay(10L) animationProgress += 0.05f } showSummary = true } MotionLayout( start = ConstraintSet("""{ outerParent: { width: 'spread', height: 'spread' }, checkmark: { width: 100, height: 100, start: ['outerParent', 'start', 0], end: ['outerParent', 'end', 0], top: ['outerParent', 'top', 0], bottom: ['outerParent', 'bottom', 0] } }""".trimMargin()), end = ConstraintSet("""{ outerParent: { width: 'spread', height: 'spread' }, checkmark: { width: 100, height: 100, top: ['outerParent', 'top', 0], start: ['outerParent', 'start', 0], end: ['outerParent', 'end', 0], }, body: { width: 'spread', top: ['checkmark', 'bottom', 16], start: ['outerParent', 'start', 0], end: ['outerParent', 'end', 0] } }""".trimMargin() ), progress = animationProgress, modifier = Modifier.fillMaxSize().padding(bottom = 40.dp) ) { // outerParent作为MotionLayout的直接子元素 Column( modifier = Modifier .layoutId("outerParent") .fillMaxSize() ) { // 给Checkmark指定layoutId,关联MotionLayout约束 Checkmark(modifier = Modifier.layoutId("checkmark")) } // 将AnimatedVisibility移到MotionLayout直接子层级,让body的layoutId生效 AnimatedVisibility ( visible = showSummary, enter = fadeIn(), exit = fadeOut() ) { Column( modifier = Modifier .layoutId("body") .fillMaxWidth() ) { Text(text = "Header") Text(text = "Content") } } } }
关键修改点
- 把
outerParent的约束明确加入start和end的ConstraintSet,确保MotionLayout正确识别父容器。 - 将
AnimatedVisibility调整为MotionLayout的直接子元素,使layoutId("body")能被MotionLayout解析应用约束。 - 重构LaunchedEffect:用
Unit作为key确保动画逻辑只执行一次,改用while循环实现进度递增,逻辑更稳定。 - 给
Checkmark添加layoutId("checkmark"),让MotionLayout能关联约束集中的规则。
内容的提问来源于stack exchange,提问作者Suguru Tokuda
相关产品推荐
相关产品推荐

