Skip to content

底板与弹窗 (Sheets & Dialogs)

源:Material Design 对话框

本章涵盖三种覆盖层组件:标准对话框 (Dialog)、临时底部弹窗 (ModalBottomSheet) 和 持久化底板 (BottomSheetScaffold)。

1. 弹窗 (Dialogs)

警告对话框 (AlertDialog)

kotlin
if (showDialog) {
    AlertDialog(
        onDismissRequest = { showDialog = false },
        icon = { Icon(Icons.Default.Warning, null) },
        title = { Text("确认删除?") },
        text = { Text("此操作不可逆。") },
        confirmButton = {
            TextButton(onClick = { delete(); showDialog = false }) { Text("确认") }
        },
        dismissButton = {
            TextButton(onClick = { showDialog = false }) { Text("取消") }
        }
    )
}

全屏/自定义对话框 (Dialog)

kotlin
Dialog(
    onDismissRequest = { /* ... */ },
    properties = DialogProperties(usePlatformDefaultWidth = false) // 全屏关键
) {
    Surface(modifier = Modifier.fillMaxSize()) {
        Text("这是一个全屏弹窗")
    }
}

2. 模态底部弹窗 (ModalBottomSheet)

这是临时的,通常由用户交互触发,点击遮罩层会消失。

kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ModalBottomSheetSample() {
    var showSheet by remember { mutableStateOf(false) }
    val sheetState = rememberModalBottomSheetState()

    if (showSheet) {
        ModalBottomSheet(
            onDismissRequest = { showSheet = false },
            sheetState = sheetState
        ) {
            // Sheet 内容
            Column(Modifier.padding(16.dp)) {
                Text("这是一个底部弹窗")
                Button(onClick = { 
                    // 必须先隐藏动画,再销毁组件
                    scope.launch { sheetState.hide() }.invokeOnCompletion { 
                        if (!sheetState.isVisible) showSheet = false 
                    }
                }) { Text("关闭") }
            }
        }
    }
}

3. 持久化底板 (BottomSheetScaffold)

这是永久的,作为屏幕布局的一部分存在。它允许用户在查看底板内容的同时与主屏幕内容交互(不像 Modal 会遮挡)。适用于地图应用、音乐播放器等。

kotlin
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetScaffoldSample() {
    val scaffoldState = rememberBottomSheetScaffoldState()

    BottomSheetScaffold(
        scaffoldState = scaffoldState,
        sheetPeekHeight = 128.dp, // 折叠时露出的高度
        sheetContent = {
            // 底板内容
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(300.dp),
                contentAlignment = Alignment.Center
            ) {
                Text("这里是持久化底板内容")
            }
        }
    ) { innerPadding ->
        // 主屏幕内容
        Box(Modifier.padding(innerPadding)) {
            Text("主屏幕内容")
        }
    }
}

4. 提示框 (Snackbar)

提示框通常需要依附于 ScaffoldBottomSheetScaffold

kotlin
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }

Scaffold(
    snackbarHost = { SnackbarHost(snackbarHostState) }
) {
    Button(onClick = {
        scope.launch {
            snackbarHostState.showSnackbar("操作成功")
        }
    }) { Text("Show") }
}