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

Jetpack Compose中自定义可复用Snackbar动画失效问题及重构方案

问题原因分析
  • 你通过if(errorMsg.isNotEmpty())条件渲染Snackbar组件,导致组件仅在显示时才被加入Compose树,AnimatedVisibility的visible初始值就是true,无法触发入场动画;隐藏时组件直接被移除,退场动画也没有执行机会。
  • 组件内部LaunchedEffect(Unit)的无限循环会持续运行定时器,即使Snackbar已隐藏,存在无效资源占用风险。
重构方案

1. 重构CealSnackbar组件

将显示状态交由外部控制,确保组件始终存在于Compose树中,让AnimatedVisibility能正常处理动画逻辑:

@Composable
fun CealSnackbar(
    title: String,
    isVisible: Boolean,
    onDismiss: () -> Unit,
    durationMillis: Long = 4000
) {
    AnimatedVisibility(
        visible = isVisible,
        enter = slideInVertically(initialOffsetY = { -it }) + fadeIn(),
        exit = slideOutVertically(targetOffsetY = { -it }) + fadeOut(),
        modifier = Modifier.fillMaxWidth()
    ) {
        Card(
            modifier = Modifier
                .padding(horizontal = 20.dp, vertical = 10.dp)
                .statusBarsPadding(),
            backgroundColor = MaterialTheme.colors.primaryVariant,
            shape = RoundedCornerShape(10.dp),
            elevation = 20.dp
        ) {
            Row(
                modifier = Modifier.padding(12.dp),
                horizontalArrangement = Arrangement.Start,
                verticalAlignment = Alignment.CenterVertically
            ) {
                Image(
                    painter = painterResource(id = R.drawable.error_circle),
                    contentDescription = "错误提示图标"
                )
                Text(
                    text = title,
                    fontFamily = FontFamily(Font(R.font.inter_medium)),
                    fontSize = 12.sp,
                    color = MaterialTheme.colors.primary,
                    modifier = Modifier.padding(start = 10.dp)
                )
            }
        }
    }

    // 仅在显示状态激活时启动自动隐藏定时器
    LaunchedEffect(isVisible) {
        if (isVisible) {
            delay(durationMillis)
            onDismiss()
        }
    }
}

2. 正确使用组件

在业务Composable中通过StateFlow状态控制Snackbar的显示/隐藏,确保动画触发逻辑正常:

// ViewModel中保持原有StateFlow逻辑
private val _errorMsg = MutableStateFlow("")
val errorMsg: StateFlow<String> = _errorMsg

fun setErrorMsg(msg: String) {
    _errorMsg.value = msg
}

// 业务Composable中
val errorMsg by registrationViewModel.errorMsg.collectAsState()
// 本地维护Snackbar的显示状态和当前标题
val isSnackbarVisible = remember { mutableStateOf(false) }
val currentSnackbarTitle = remember { mutableStateOf("") }

// 监听错误消息变化,同步更新Snackbar状态
LaunchedEffect(errorMsg) {
    if (errorMsg.isNotEmpty()) {
        currentSnackbarTitle.value = errorMsg
        isSnackbarVisible.value = true
    } else {
        isSnackbarVisible.value = false
    }
}

// 始终渲染Snackbar,通过isVisible参数控制显示隐藏
CealSnackbar(
    title = currentSnackbarTitle.value,
    isVisible = isSnackbarVisible.value,
    onDismiss = {
        registrationViewModel.setErrorMsg("")
        isSnackbarVisible.value = false
    }
)
重构说明
  • 移除条件渲染逻辑,让CealSnackbar始终存在于Compose树中,确保AnimatedVisibility能捕捉到visible状态的变化,触发入场/退场动画。
  • 将LaunchedEffect的触发key改为isVisible,避免无限循环,仅在Snackbar显示时启动自动隐藏定时器。
  • 优化组件内部Modifier写法,移除冗余的背景设置,调整布局参数更合理。
  • 通过本地状态缓存错误消息和显示状态,确保每次新的错误消息都能正确触发Snackbar的显示动画。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 08:56:06