[英]How to show snackbar with a button onclick in Jetpack Compose
I want to show snackbar with a button onclick in Jetpack Compose我想在 Jetpack Compose 中显示带有按钮 onclick 的小吃店
I tried this我试过这个
Button(onClick = {
Snackbar(action = {}) {
Text("hello")
}
}
But AS said "@Composable invocations can only happen from the context of a @Composable function"但是正如所说“@Composable 调用只能在@Composable 函数的上下文中发生”
Shall you give me a nice program.你能给我一个好的程序吗?
I wish it can run in Button.onclick()我希望它可以在 Button.onclick() 中运行
You'll need two things:你需要两件事:
SnackbarHostState
- which will manage the state of your Snackbar
( you would usually get that from the ScaffoldState
) SnackbarHostState
- 它将管理您的Snackbar
的状态(您通常会从ScaffoldState
获得它)CoroutineScope
- which will be responsible for showing
your Snackbar
CoroutineScope
- 负责showing
你的Snackbar
@Composable
fun SnackbarDemo() {
val scaffoldState = rememberScaffoldState() // this contains the `SnackbarHostState`
val coroutineScope = rememberCoroutineScope()
Scaffold(
modifier = Modifier,
scaffoldState = scaffoldState // attaching `scaffoldState` to the `Scaffold`
) {
Button(
onClick = {
coroutineScope.launch { // using the `coroutineScope` to `launch` showing the snackbar
// taking the `snackbarHostState` from the attached `scaffoldState`
val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(
message = "This is your message",
actionLabel = "Do something."
)
when (snackbarResult) {
Dismissed -> Log.d("SnackbarDemo", "Dismissed")
ActionPerformed -> Log.d("SnackbarDemo", "Snackbar's button clicked")
}
}
}
) {
Text(text = "A button that shows a Snackbar")
}
}
}
Building on Bartek's answer you can also use Compose's side-effects .基于 Bartek 的回答,您还可以使用Compose 的副作用。 Then you don't have to manage the CoroutineScope itself.
然后您不必管理CoroutineScope本身。
Since Composables
itself should be side-effect free , it is recommended to make use of Compose's Effect APIs
so that those side effects are executed in a predictable manner.由于
Composables
本身应该没有副作用,因此建议使用Compose's Effect APIs
,以便以可预测的方式执行这些副作用。
In your specific case you can use the LaunchedEffect API
to run suspend functions in the scope of a composable.在您的特定情况下,您可以使用
LaunchedEffect API
在可组合的 scope 中运行挂起功能。 The example code would look like the following:示例代码如下所示:
@Composable
fun SnackbarDemo() {
val scaffoldState = rememberScaffoldState() // this contains the `SnackbarHostState`
val (showSnackBar, setShowSnackBar) = remember {
mutableStateOf(false)
}
if (showSnackBar) {
LaunchedEffect(scaffoldState.snackbarHostState) {
// Show snackbar using a coroutine, when the coroutine is cancelled the
// snackbar will automatically dismiss. This coroutine will cancel whenever
// `showSnackBar` is false, and only start when `showSnackBar` is true
// (due to the above if-check), or if `scaffoldState.snackbarHostState` changes.
val result = scaffoldState.snackbarHostState.showSnackbar(
message = "Error message",
actionLabel = "Retry message"
)
when (result) {
SnackbarResult.Dismissed -> {
setShowSnackBar(false)
}
SnackbarResult.ActionPerformed -> {
setShowSnackBar(false)
// perform action here
}
}
}
}
Scaffold(
modifier = Modifier,
scaffoldState = scaffoldState // attaching `scaffoldState` to the `Scaffold`
) {
Button(
onClick = {
setShowSnackBar(true)
}
) {
Text(text = "A button that shows a Snackbar")
}
}
}
If you use new Material3 there is new field in Scaffold: "snackbarHost"如果您使用新的 Material3,Scaffold 中有一个新字段:“snackbarHost”
val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Scaffold(
snackbarHost = { SnackbarHost(snackbarHostState) },
...
// for showing snackbar in onClick for example:
scope.launch {
snackbarHostState.showSnackbar(
"Snackbar Test"
)
}
Just in case you need a snackbar as a separate function to call in other screens with custom parameters, and without using scaffold, I use this below.以防万一您需要一个 snackbar 作为单独的 function 来使用自定义参数调用其他屏幕,而不使用脚手架,我在下面使用它。
The parameter openSnackbar: (Boolean) -> Unit
is used to reset the opening condition var openMySnackbar by remember { mutableStateOf(false) }
and allow open snackbar other times.参数
openSnackbar: (Boolean) -> Unit
用于var openMySnackbar by remember { mutableStateOf(false) }
并允许其他时间打开 snackbar。
@Composable
fun SnackbarWithoutScaffold(
message: String,
showSb: Boolean,
openSnackbar: (Boolean) -> Unit
) {
val snackState = remember { SnackbarHostState() }
val snackScope = rememberCoroutineScope()
SnackbarHost(
modifier = Modifier,
hostState = snackState
){
Snackbar(
snackbarData = it,
backgroundColor = Color.White,
contentColor = Color.Blue
)
}
if (showSb){
LaunchedEffect(Unit) {
snackScope.launch { snackState.showSnackbar(message) }
openSnackbar(false)
}
}
}
And used like this:并像这样使用:
@Composable fun MyScreen() { @Composable fun MyScreen() {
var openMySnackbar by remember { mutableStateOf(false) }
var snackBarMessage by remember { mutableStateOf("") }
Column() {
Button(
shape = RoundedCornerShape(50.dp),
onClick = {
openMySnackbar = true
snackBarMessage = "Your message"
},
) {
Text(text = "Open Snackbar")
}
SnackbarWithoutScaffold(snackBarMessage, openMySnackbar, , {openMySnackbar = it})
}
} }
You can use this你可以用这个
@Composable
fun MainScreen() {
val coroutineScope = rememberCoroutineScope()
val showSnackBar: (
message: String?,
actionLabel: String,
actionPerformed: () -> Unit,
dismissed: () -> Unit
) -> Unit = { message, actionLabel, actionPerformed, dismissed ->
coroutineScope.launch {
val snackBarResult = scaffoldState.snackbarHostState.showSnackbar(
message = message.toString(),
actionLabel = actionLabel
)
when (snackBarResult) {
SnackbarResult.ActionPerformed -> actionPerformed.invoke()
SnackbarResult.Dismissed -> dismissed.invoke()
}
}
}
showSnackBar.invoke(
"YOUR_MESSAGE",
"ACTION_LABEL",
{
//TODO ON ACTION PERFORMED
},
{
//TODO ON DISMISSED
}
)
}
Let me add an other answer, i was not using scaffold and was using state hoisting让我添加另一个答案,我没有使用脚手架,而是使用 state 吊装
val loginState by viewModel.uiState.collectAsState() // STATE HOISTING
val snackbarHostState = remember { SnackbarHostState() }
val message = stringResource(id = R.string.error_login)
if (loginState.snackbarVisible) { // When passing true, show snackbar
LaunchedEffect(snackbarHostState) {
snackbarHostState.showSnackbar(
message = message,
actionLabel = "Do something."
)
}
}
Box(
modifier = Modifier.fillMaxSize()
) {
// Your screen content
// ...
SnackbarHost(
hostState = snackbarHostState,
modifier = Modifier.align(Alignment.BottomCenter)
) // Snackbar location
}
First of all you need a SnackbarHostState, you can pass this state down to your composable where you want to trigger a snackbar message.首先,您需要一个 SnackbarHostState,您可以将此 state 传递给您想要触发小吃栏消息的组合。 or if you use a scaffold use that one
scaffoldState.snackbarHostState
或者,如果您使用脚手架,请使用该
scaffoldState.snackbarHostState
val snackbarHostState = remember { SnackbarHostState() }
Showing a snackbar is a side effect and should be wrapped in a LaunchEffect
Composable显示一个snackbar是一个副作用,应该被包裹在一个
LaunchEffect
Composable中
@Composable
fun MyScreen(
scaffoldState: ScaffoldState = rememberScaffoldState()
) {
Scaffold(scaffoldState = scaffoldState) {
Button(onClick = {
LaunchedEffect(scaffoldState.snackbarHostState) {
scaffoldState.snackbarHostState.showSnackbar(
message = "hello world",
actionLabel = "Retry"
)
}
// ...
}
}
See more information here https://developer.android.com/topic/architecture/ui-layer/events#consuming-trigger-updates在此处查看更多信息https://developer.android.com/topic/architecture/ui-layer/events#sumption-trigger-updates
and https://developer.android.com/jetpack/compose/side-effects和https://developer.android.com/jetpack/compose/side-effects
You can show snackBar without Scaffold using SnackBar
Composable either.您也可以使用
SnackBar
Composable 来显示没有 Scaffold 的snackBar。
Box(modifier = Modifier.fillMaxSize()) {
var showSnackbar by remember {
mutableStateOf(false)
}
LaunchedEffect(key1 = showSnackbar) {
if (showSnackbar) {
delay(2000)
showSnackbar = false
}
}
Column {
Button(onClick = {
showSnackbar = !showSnackbar
}) {
Text("Show Snackbar")
}
}
if (showSnackbar) {
androidx.compose.material.Snackbar(modifier = Modifier.align(Alignment.BottomStart),
action = {
Text(text = "Action",
color = Color(0xffCE93D8),
modifier = Modifier.clickable {
showSnackbar = false
}
)
}) {
androidx.compose.material.Text("Message")
}
}
}
LaunchedEffect is to remove it from composition. LaunchedEffect 是将其从合成中移除。 You can use a slide animation if you want to.
如果您愿意,可以使用幻灯片 animation。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.