使用 Scaffold 在 Material Design 3 中展示 Snackbar

17

决定在我的新Jetpack Compose项目中尝试使用Material Design 3。一切都很顺利,直到我需要在遇到问题时显示Snackbar。

MD2中,这非常容易,您可以在协程范围内使用SnackbarHostState.showSnackbar()函数在Scaffold中显示snackbar。我发现您只需要从Material Library导入androidx.compose.material.rememberScaffoldState即可。

import androidx.compose.material.rememberScaffoldState


@Composable
fun MyScreenInMaterial2() {
    val scaffoldState = rememberScaffoldState()
}

当我在MD3中尝试相同操作时,rememberScaffoldState()函数未被解决。 enter image description here

对于那些已经了解MD3世界的人们,如何在Scaffold中显示Snackbar呢?我已经检查过文档和在线资源,但还没有找到解决方案。


1
https://dev59.com/glEG5IYBdhLWcg3wLFwk - Martin Zeitler
谢谢@MartinZeitler,看起来我们得等MD团队来做些什么。我会把问题留在这里,以防其他人遇到同样的问题... - Tonnie
3个回答

23

这里有一个来自官方文档的例子。

val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope()
Scaffold(
    snackbarHost = { SnackbarHost(snackbarHostState) },
    floatingActionButton = {
        var clickCount by remember { mutableStateOf(0) }
        ExtendedFloatingActionButton(
            onClick = {
                // show snackbar as a suspend function
                scope.launch {
                    snackbarHostState.showSnackbar(
                        "Snackbar # ${++clickCount}"
                    )
                }
            }
        ) { Text("Show snackbar") }
    },
    content = { innerPadding ->
        Text(
            text = "Body content",
            modifier = Modifier.padding(innerPadding).fillMaxSize().wrapContentSize()
        )
    }
)

4
天啊,我怎么会错过这个。MDC3去掉了在MD2中可以使用的scaffoldState参数,如:scaffoldState.snackbarHostState.showSnackbar()。现在你需要使用_remember { SnackbarHostState() }_来代替,就像这样使用snackbarHostState.showSnackbar()。因此,在MD3中不再需要ScaffoldState了!! 感谢@Damian - Tonnie
1
避免在此处使用CoroutineScope,因为您将收到警告:“CoroutineCreationDuringComposition”。我基于这个答案添加了一个答案。TLDR:用LaunchedEffect替换CoroutineScope。 - Joaquin Iurchuk

6

根据Damian Petla的答案

如果使用CoroutineScope,则会收到一个警告,提示CoroutineCreationDuringComposition

官方文档建议使用LaunchedEffect代替以避免此副作用。

因此,建议的代码如下所示:

val snackbarHostState = remember { SnackbarHostState() }

Scaffold(
    snackbarHost = { SnackbarHost(snackbarHostState) },
    floatingActionButton = {
        var clickCount by remember { mutableStateOf(0) }
        ExtendedFloatingActionButton(
            onClick = {
                LaunchedEffect(snackbarHostState) {
                    snackbarHostState.showSnackbar(
                        "Snackbar # ${++clickCount}"
                    )
                }
            }
        ) { Text("Show snackbar") }
    },
    content = { innerPadding ->
        Text(
            text = "Body content",
            modifier = Modifier.padding(innerPadding).fillMaxSize().wrapContentSize()
        )
    }
)

2
这是正确的,Docs支持它,感谢您的见解。 - Tonnie
内容是指文本、图像、音频、视频或其他形式的信息,用于传达特定的信息或表达特定的意义。在许多情况下,内容是用来吸引读者或观众的注意力,并传达特定的信息或概念。它可以用于教育、娱乐、传媒、广告等各个领域。为什么内容是必要的呢?内容的重要性在于它可以帮助传达者有效地与受众沟通,并传递所需的信息。无论是在商业领域还是在个人领域,内容都是建立品牌形象、吸引潜在客户、提供有价值的信息的关键因素。虽然文档中可能没有明确解释内容是什么以及为什么它是必要的,但理解内容的重要性可以帮助我们更好地利用它来实现我们的目标。通过创造有吸引力、有用的内容,我们可以吸引更多的读者或观众,并与他们建立起良好的关系。这将有助于提高我们的影响力、扩大我们的受众,并最终实现我们的目标。 - undefined
@MuhammadSarimMehdi 你有一个像视图基础一样的脚手架。你想在其中看到一些东西,对吧?嗯,那个“东西”就是内容。在Scaffold的文档中解释了content - undefined

4
在我的情况下,出现了编译错误:
@Composable invocations can only happen from the context of a @Composable function

onClickChip lambda内调用LaunchedEffect时。

有一个替代方案,受到这个答案的启发。

对于那些想要在lambda中显示Compose Snackbar(无@Composable)的人来说,这是一个替代选项。

@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun ScreenA() {
    val snackbarHostState = remember { SnackbarHostState() }
    val snackbarMessage = "Succeed!"
    val showSnackbar = remember { mutableStateOf(false) }

    LaunchedEffect(showSnackbar.value) {
        if (showSnackbar.value)
            snackbarHostState.showSnackbar(snackbarMessage)
    }
    Surface {
        Scaffold(
            snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
        ) {
            ChipIcon(
                text = "Click me: ${showSnackbar.value}",
                onClickChip = {
                    // unable to use LaunchedEffect in here
                    showSnackbar.value = !showSnackbar.value
                }
            )
        }
    }
}

// Custom Composable
@Composable
fun ChipIcon(
    text: String,
    onClickChip: () -> Unit, // no @Composable, unable to use LaunchedEffect in this lambda
) {
    Row(
        modifier = Modifier
            .clickable(
                enabled = true,
                onClick = {
                    onClickChip()
                }
            ),
    ) {
        Text(text = text)
    }
}

观察showSnackbar.value的变化,而不是在LaunchedEffect中进行取消/重新启动,如果showSnackbar.value== true,则显示 snackbar,否则不执行任何操作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接