You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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")
                }
            }
        }
    }
}
问题根源
  1. AnimatedVisibility嵌套在outerParent的Column中,MotionLayout无法识别body的布局ID——因为MotionLayout只会解析直接子元素的layoutId。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 13:19:55