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

能否跟踪AnimatedVisibility动画结束?实现元素显隐切换遇问题求助

问题:Compose中点击按钮实现元素先消失再重新显示的动画异常

想要实现点击按钮后,元素先执行退出动画消失,再执行进入动画重新显示的效果,但目前元素重新显示时会出现在退出动画移动到的同一侧,达不到预期。

原代码如下:

@Composable
fun Greeting() {

    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {

        val visibleState = remember { MutableTransitionState(initialState = true).apply { targetState = true } }

        AnimatedVisibility(
            visibleState = visibleState,
            enter = slideInHorizontally(
                animationSpec = tween(100, easing = LinearEasing),
                initialOffsetX = { it }
            ),
            exit = slideOutHorizontally (
                animationSpec = tween(100, easing = LinearEasing),
                targetOffsetX = { -it }
            )
        ) {
            RedBox()
        }

        Button {
            visibleState.targetState = false
        }

        DisposableEffect(visibleState.currentState) {
            onDispose {
                visibleState.targetState = true
            }
        }

    }

}
解决方案

问题原因

原代码中使用DisposableEffect监听currentState,当currentState变为false时会立即触发onDispose并设置targetState=true,导致退出动画还未完成就开始执行进入动画,状态切换冲突。同时,未确保两次动画之间有明确的间隔和状态重置。

修改后的代码

@Composable
fun Greeting() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        val visibleState = remember { MutableTransitionState(true) }
        val animationDuration = 100 // 和动画时长保持一致

        AnimatedVisibility(
            visibleState = visibleState,
            enter = slideInHorizontally(
                animationSpec = tween(animationDuration, easing = LinearEasing),
                initialOffsetX = { it } // 从右侧滑入
            ),
            exit = slideOutHorizontally(
                animationSpec = tween(animationDuration, easing = LinearEasing),
                targetOffsetX = { -it } // 向左侧滑出
            )
        ) {
            RedBox()
        }

        Button(onClick = {
            // 仅当当前状态为显示时,触发隐藏
            if (visibleState.currentState) {
                visibleState.targetState = false
            }
        }) {
            Text("触发动画")
        }

        // 监听targetState变化,等待退出动画完成后重新显示
        LaunchedEffect(visibleState.targetState) {
            if (!visibleState.targetState) {
                delay(animationDuration.toLong())
                visibleState.targetState = true
            }
        }
    }
}

@Composable
fun RedBox() {
    Box(
        modifier = Modifier
            .size(50.dp)
            .background(Color.Red)
    )
}

关键修改点

  • 用LaunchedEffect替代DisposableEffect:监听targetState的变化,当元素开始执行退出动画(targetState=false)时,等待与动画时长一致的时间后,再将targetState设为true,确保退出动画完全结束后才启动进入动画。
  • 增加状态判断:点击按钮时仅在元素处于显示状态时触发隐藏,避免重复触发导致的状态混乱。
  • 统一动画时长:将动画时长提取为变量,确保延迟时间和动画时长匹配,保证动画衔接自然。

这样修改后,点击按钮元素会先向左滑出消失,等待动画结束后再从右侧滑入重新显示,符合预期效果。

内容的提问来源于stack exchange,提问作者CyllXap4ok

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 11:43:15