底板与弹窗 (Sheets & Dialogs)
本章涵盖三种覆盖层组件:标准对话框 (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)
提示框通常需要依附于 Scaffold 或 BottomSheetScaffold。
kotlin
val scope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) }
) {
Button(onClick = {
scope.launch {
snackbarHostState.showSnackbar("操作成功")
}
}) { Text("Show") }
}