由于业务需求,需要一个底部弹出的Dialog,本菜坤直接就拿出Dialog来应对,布局写完了,发现怎么没有弹出方向的控制,然后本菜坤就Chatgpt了,告诉我用可以用 ModalBottomSheet 来实现,我就框框一顿贴,布局也弄完了,就发现怎么没有点击空白不消失的功能,这样不行,那也不符合,本坤就自己通过动画的方式实现了一个简单的底部弹出Dialog,上代码:
/**
* 自定义底部弹窗
* @param isVisible 是否显示弹窗
* @param dismissOnClickOutside 是否可以点击空白取消弹窗
* @param backgroundColor 弹窗蒙层背景色
* @param paddingValues 内间距
* @param onDismiss 弹窗取消的回调
* @param content 弹窗主布局
* */
@Composable
fun CustomBottomDialog(
isVisible: Boolean,
dismissOnClickOutside: Boolean = true,
backgroundColor: Color = Color.Black,
paddingValues: PaddingValues = PaddingValues(0.dp),
onDismiss: () -> Unit,
content: @Composable () -> Unit
) {
var shouldShow by remember { mutableStateOf(isVisible) }
val screenHeight =
LocalDensity.current.run { with(LocalConfiguration.current) { screenHeightDp.dp.toPx() } }
val offsetY = remember { Animatable(screenHeight) }
val backgroundAlpha = remember { Animatable(0f) } // 初始透明度为 0
LaunchedEffect(isVisible) {
if (isVisible) {
shouldShow = true
launch {
//弹出动画
offsetY.animateTo(
targetValue = 0f,
animationSpec = tween(durationMillis = 300)
)
}
launch {
// 显示时背景透明度从 0f 渐变到 0.5f
backgroundAlpha.animateTo(
targetValue = 0.5f,
animationSpec = tween(durationMillis = 300)
)
}
} else {
launch {
//回收动画
offsetY.animateTo(
targetValue = screenHeight,
animationSpec = tween(durationMillis = 300)
)
}
launch {
// 隐藏时背景透明度从 0.5f 渐变到 0f
backgroundAlpha.animateTo(
targetValue = 0f,
animationSpec = tween(durationMillis = 300)
)
}
shouldShow = false // 关闭动画结束后隐藏布局
}
}
if (shouldShow || backgroundAlpha.value > 0f) {
Box(
modifier = Modifier
.fillMaxSize()
.background(backgroundColor.copy(alpha = backgroundAlpha.value))
.clickable(
onClick = {
if (dismissOnClickOutside) {
onDismiss()
}
}, // 点击背景关闭弹窗
indication = null,
interactionSource = remember { MutableInteractionSource() }
)
) {
Box(
modifier = Modifier
.offset { IntOffset(0, offsetY.value.toInt()) }
.fillMaxWidth()
.padding(paddingValues)
.align(Alignment.BottomCenter) // 弹窗从底部弹出
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null
) {
// 防止事件穿透
}
) {
content()
}
}
}
}