【问题标题】:Snackbar not showing in Jetpack ComposeJetpack Compose 中未显示 Snackbar
【发布时间】:2021-08-04 22:37:28
【问题描述】:

我的屏幕有一个按钮,点击它,我想在短时间内显示一个小吃栏。所以这是我的可组合:

@Composable
fun RegisterLocation(
    navController: NavController,
    locationVm: LocationViewModel = LocationViewModel()
) {
    val uiState = locationVm.locationState.collectAsState(LocationUiState.Init)
    val snackbarHostState = remember { SnackbarHostState() }
    when (uiState.value) {
        is LocationUiState.Init -> RegisterLocation(locationVm, snackbarHostState)
        is LocationUiState.Invalid -> {
            LaunchedEffect(locationVm.locationState.value) {
                snackbarHostState.showSnackbar(
                    "Failed Creating Location. Try Again!!!"
                )
            }
        }
        is LocationUiState.Valid -> navController.popBackStack()
    }
}

@Composable
fun RegisterLocation(locationVm: WLocationViewModel, snackbarHostState: SnackbarHostState) {
    Box(Modifier.fillMaxSize()) {
        Column {
            SubmitLocation(locationVm)
        }

        Column(
            Modifier
                .fillMaxWidth()
                .align(Alignment.BottomCenter)
        ) {
            Text(text = "Hello") // <-- This is a dummy text and it shows in the UI. Moreover, it animates up to give space for snackbar. But the snackbar doesn't show up :(
            RegistrationError(snackbarHostState)
        }
    }
}

@ExperimentalCoroutinesApi
@Composable
fun SubmitLocation(vm: WLocationViewModel) {
    Button(
        onClick = { vm.onCreate(LocationUiState.Invalid) },
        modifier = Modifier.fillMaxWidth()
    ) {
        Text(text = "Submit Location")
    }
}

@Composable
fun RegistrationError(hostState: SnackbarHostState) {
    SnackbarHost(
        hostState = hostState,
        modifier = Modifier.fillMaxWidth(),
        snackbar = {
            Snackbar {
                Text(text = "Hello", color = Color.White)
            }
        }
    )
}

这段代码有什么问题?任何帮助将不胜感激。

【问题讨论】:

  • 你试过用 coroutineScope.launch 代替 LaunchedEffect 吗?
  • 检查 coroutineScope.launch(Dispatchers.Main) {} 不工作。直接启动协程也会出现以下错误。 Calls to launch should happen inside a LaunchedEffect and not composition@GabrieleMariotti

标签: android android-jetpack-compose


【解决方案1】:

SnackbarHostState 在Scaffold 中显示snackbar,或者可以作为参数传递给SnackBar 可组合。

为什么snackbar主机不显示snackbar?

每当state 更改时,snackBar 确实不 有任何Scaffold 或SnackbarHost 来显示内容。 (snackbarHostState 也可以在编写你已经完成的 SnackBar 时传递 - 检查解决方案 2)

在无效状态期间,您既没有使用Scaffold,也没有使用snackBarHostState 编写SnackBarHost。

解决方案 1

我们需要将SnackbarHostState 附加到Scaffold 以使其显示。

when (uiState.value) {
   is LocationUiState.Init -> RegisterLocation(locationVm, snackbarHostState)
   is LocationUiState.Invalid -> {
         LaunchedEffect(locationVm.locationState.value) {
            snackbarHostState.showSnackbar(
                    "Failed Creating Location. Try Again!!!",
                    "label",
                    duration = SnackbarDuration.Long
            )
         }
         Scaffold(
                // attach snackbar host state to the scaffold
             scaffoldState = rememberScaffoldState(snackbarHostState = snackbarHostState),
             content = { } // content is not mandatory
         )
   }
}

解决方案 2:-

您永远不会在无效状态下编写RegisterLocation(locationVm, snackbarHostState)。根据代码,如果我们在 Invalid 状态下组合 RegisterLocation 可组合,那么作为可组合添加的快餐栏也应该显示出来。

when (uiState.value) {
        is LocationUiState.Init -> RegisterLocation(locationVm, snackbarHostState)
        is LocationUiState.Invalid -> {
            RegistrationError(hostState = snackbarHostState)
            LaunchedEffect(locationVm.locationState.value) {
                snackbarHostState.showSnackbar(
                    "Failed Creating Location. Try Again!!!",
                    "label",
                    duration = SnackbarDuration.Long
                )
            }
        }
}

更新(详细说明)

Compose 在底层维护了一个 GroupArray ,它包含根据位置绘制在屏幕上的所有 UI 元素。因此,在重新组合期间,它仅比较并显示在重新组合期间绘制的 UI 元素,并删除在先前组合期间构建的其他组件 redundant (就像我们在重新组合期间不访问的 UI 组件一样被删除)。

对于您的情况:- states 完全 不同,我们在 第二 合成期间从未访问 Init 状态。因此,compose 在重新组合期间看到我们从未访问过Init 状态,因此它会从内存中删除Init UI。 (在后台使用positional memoisation)。

请查看compose under the hood

【讨论】:

  • 这并不完全正确。您可以使用没有 Scaffold 的 SnackbarHost。
  • 我们也可以将SnackbarHostState 作为SnackBarHost ryt 的参数传递给我在解决方案2 中提到的SnackbarHost( hostState = hostState, ....)。然后将SnackBarHost 与主机状态组合起来也应该可以工作。因此,当东道国调用showSnackBar(..) 时,SnackbarHost 应该显示小吃店@GabrieleMariotti
  • SnackbarHost 可以独立工作,但SnackbarHostState 应该需要一个可组合显示(Scaffold 或SnackbarHost)ryt 吗? @GabrieleMariotti
  • 我已经在答案中提到了。
  • @SantanuSur,在无效状态下撰写 RegisterLocation 是否会重新绘制整个屏幕,而不仅仅是绘制小吃栏?如果可组合组件有大块的 ui 组件要绘制怎么办?
猜你喜欢
  • 2021-04-26
  • 2021-08-05
  • 1970-01-01
  • 2021-08-28
  • 2022-06-25
  • 2022-06-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多