JetpackCompose 自定义底部弹窗的简单实现

461 阅读1分钟

由于业务需求,需要一个底部弹出的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()
            }
        }
    }

}