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
相关产品推荐
相关产品推荐

