Compose笔记(四十七)--SnackbarHost
这一节主要了解一下Compose中的SnackbarHost,在Jetpack Compose开发中,SnackbarHost是管理Snackbar显示的核心组件,通常与Scaffold结合使用,提供统一的底部消息提示区域。
API
SnackbarHost组件,是一个容器组件,用于在界面中指定Snackbar的显示位置,并将Snackbar与SnackbarHostState关联,使其能够响应状态变化。
构造函数关键参数:
hostState:必填,类型为SnackbarHostState,用于控制Snackbar的显示状态。
modifier:可选,用于设置布局样式。
snackbar:可选,用于自定义Snackbar的样式。
SnackbarHostState状态类,管理Snackbar的状态数据,包括当前显示的Snackbar内容、交互结果等
核心属性和方法:
currentSnackbarData:当前显示的Snackbar数据。
showSnackbar():挂起函数,用于触发Snackbar显示,返回SnackbarResult。
currentSnackbarData?.dismiss():关闭当前显示的Snackbar。
场景:
1 轻量级操作结果提示,如:删除数据后提示 “已删除”、保存设置后提示 “保存成功”等。
2 带交互的二次操作,通过actionLabel添加动作按钮,支持用户执行撤销、重试等操作。
3 长时间提示与手动关闭,使用SnackbarDuration.Indefinite设置无限期显示,配合手动关闭按钮
4 自定义样式适配主题,通过SnackbarHost的snackbar参数自定义外观,如修改背景色、文本大小等,适配应用主题。
栗子:
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.compose.viewModel
import kotlinx.coroutines.launchclass DeleteViewModel : ViewModel() {var items by mutableStateOf(listOf("项目1", "项目2", "项目3"))private setfun deleteLastItem() {if (items.isNotEmpty()) {items = items.dropLast(1)}}fun undoDelete(deletedItem: String) {items = items + deletedItem}
}@Composable
fun SnackbarExample(viewModel: DeleteViewModel = viewModel()
) {val snackbarHostState = remember { SnackbarHostState() }val scope = rememberCoroutineScope()var lastDeletedItem by remember { mutableStateOf("") }Scaffold(snackbarHost = { SnackbarHost(snackbarHostState) },topBar = {Text(text = "Snackbar示例",modifier = Modifier.padding(20.dp))}) { innerPadding ->Column(modifier = Modifier.fillMaxSize().padding(innerPadding).padding(20.dp),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {Text(text = "当前项目: ${viewModel.items.joinToString()}",modifier = Modifier.padding(bottom = 30.dp))Button(onClick = {if (viewModel.items.isNotEmpty()) {lastDeletedItem = viewModel.items.last()viewModel.deleteLastItem()scope.launch {val result = snackbarHostState.showSnackbar(message = "已删除 '$lastDeletedItem'",actionLabel = "撤销",duration = androidx.compose.material3.SnackbarDuration.Short)if (result == androidx.compose.material3.SnackbarResult.ActionPerformed) {viewModel.undoDelete(lastDeletedItem)}}} else {scope.launch {snackbarHostState.showSnackbar(message = "没有可删除的项目",duration = androidx.compose.material3.SnackbarDuration.Short)}}}) {Icon(imageVector = Icons.Default.Delete,contentDescription = "删除",modifier = Modifier.padding(end = 8.dp))Text("删除最后一项")}}}
}
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch@Composable
fun SnackbarExample() {val snackbarHostState = remember { SnackbarHostState() }val scope = rememberCoroutineScope()var inputText by remember { mutableStateOf("") }Scaffold(snackbarHost = {SnackbarHost(snackbarHostState) { data ->Snackbar(action = {if (data.visuals.actionLabel != null) {Button(onClick = { data.performAction() }) {Text(data.visuals.actionLabel ?: "")}}}) {Text(data.visuals.message)}}},topBar = {Text(text = "Snackbar Demo",modifier = Modifier.padding(20.dp))}) { innerPadding ->Column(modifier = Modifier.fillMaxSize().padding(innerPadding).padding(20.dp),horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.spacedBy(20.dp, Alignment.CenterVertically)) {OutlinedTextField(value = inputText,onValueChange = { inputText = it },label = { Text("请输入内容") },keyboardOptions = KeyboardOptions(imeAction = ImeAction.Send),keyboardActions = KeyboardActions(onSend = {submitInput(inputText, snackbarHostState, scope)}))Button(onClick = { submitInput(inputText, snackbarHostState, scope) }) {Text("提交")}Button(onClick = {scope.launch {snackbarHostState.currentSnackbarData?.dismiss()}},enabled = snackbarHostState.currentSnackbarData != null) {Text("关闭当前提示")}}}
}private fun submitInput(text: String,snackbarHostState: SnackbarHostState,scope: kotlinx.coroutines.CoroutineScope
) {scope.launch {if (text.isBlank()) {snackbarHostState.showSnackbar(message = "错误:输入不能为空!",duration = androidx.compose.material3.SnackbarDuration.Short)} else {snackbarHostState.showSnackbar(message = "成功:已提交内容 '$text'",actionLabel = "知道了",duration = androidx.compose.material3.SnackbarDuration.Long)}}
}
注意:
1 必须与Scaffold配合使用,SnackbarHost通常作为Scaffold的snackbarHost参数传入,确保Snackbar显示在正确的层级。
2 挂起函数需在协程中调用,showSnackbar()和dismiss()是挂起函数,必须在协程作用域中调用,否则会编译报错。
3 显示时长控制,避免长时间显示,除非提供明确的关闭方式。