如何在 Material 3 Jetpack Compose Android 中实现 BottomSheet

Rah*_*eem 33 android kotlin android-jetpack-compose android-jetpack-compose-material3

我知道如何使用 Material 2 Jetpack Compose 实现 BottomSheet BottomSheetScaffold

但Material 3中没有BottomSheetScaffold。而且,官方样本中也没有关于BottomSheet的内容。

Mar*_*ski 25

所以我能够让它发挥作用!

看来,截至今天,BottomSheetScaffold在 Material3 上尚不可用,这在我发现的本期中讨论:https ://issuetracker.google.com/issues/229839039

我引用了谷歌开发者回复中的重要部分:

对于 Swipeable,我们并没有处于一个超级轻松的境地。目前它有许多关键错误需要首先解决(我们正在解决这个问题),这就是为什么我们暂时限制 M3 中 Swipeable 暴露的表面。我们未来几个月的计划是专注于这一特定领域并改善开发人员体验。

Jetpack Compose 的 Material 3 仍处于 alpha 阶段 - 这意味着我们认为组件已做好生产准备,但 API 形状在 alpha 阶段是灵活的。这为我们提供了迭代的空间,同时从开发人员那里获得真实的反馈,这最终有助于改善您的体验。同时,复制粘贴未(完全)实现或在 alpha 版本中公开的组件的源代码可能是一件好事!在 API 形式仍然灵活的情况下拥有源代码会给您带来许多好处,例如即使 API 发生变化,也可以轻松更新依赖项,并允许您按照自己的节奏发展组件。

所以我只是听从了建议并将其复制粘贴BottomSheetScaffold到我的项目中。当然,由于缺少一些类和一些小的 API 更改,它并没有立即起作用。最后,我能够通过提取和破解以下类并将它们添加到我的项目中来使其工作:

  • BottomSheetScaffold.kt
  • Drawer.kt
  • Strings.kt
  • Swipeable.kt

如果您想尝试,我已经用源代码创建了要点: https://gist.github.com/Marlinski/0b043968c2f574d70ee6060aeda54882

您必须更改导入以使其适用于您的项目,并"-Xjvm-default=all"通过将以下内容添加到 gradle 文件的 部分来添加选项android{}

android{ 
   ...
   kotlinOptions {
        freeCompilerArgs += ["-Xjvm-default=all"]

        // "-Xjvm-default=all" option added because of this error:
        // ... Inheritance from an interface with '@JvmDefault' members is only allowed with -Xjvm-default option
        // triggered by porting BottomSheetScaffold for Material3 on Swipeable.kt:844
   }
}
Run Code Online (Sandbox Code Playgroud)

它对我来说非常有效,将保留这个解决方案,直到它在material3中得到正式支持。

希望能帮助到你!

  • 太棒了,谢谢你在上菜之前直接把它从烤箱里拿出来。 (3认同)

Jee*_*cha 16

我们终于在Material3中拥有了 ModalBottomSheet 。

更新 通过新的更新,如果您想启用或禁用,可以使用边缘到边缘功能

    val windowInsets = if (edgeToEdgeEnabled)
        WindowInsets(0) else BottomSheetDefaults.windowInsets


var openBottomSheet by rememberSaveable { mutableStateOf(false) }
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded  = true)



// Sheet content
if (openBottomSheet) {
    ModalBottomSheet(
        onDismissRequest = { openBottomSheet = false },
        sheetState = bottomSheetState,
    ) {
        Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
            Button(
                // Note: If you provide logic outside of onDismissRequest to remove the sheet,
                // you must additionally handle intended state cleanup, if any.
                onClick = {
                    scope.launch { bottomSheetState.hide() }.invokeOnCompletion {
                        if (!bottomSheetState.isVisible) {
                            openBottomSheet = false
                        }
                    }
                }
            ) {
                Text("Hide Bottom Sheet")
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更多内容请阅读:链接

  • 我变得比材料库的普通 ModalBottomSheet 更糟糕,之前您可以使用底部工作表状态来显示/隐藏,但现在您必须通过标志来控制它,因为如果您使用状态并尝试隐藏它,它保持在那里有一点偏移,你无法与后面的屏幕交互......哈哈 (5认同)
  • 由于“androidx.compose.material3:material3:1.1.0-alpha08”,您不能使用“rememberSheetState”和“skipHalfExpanded”,因此您应该将代码从“val BottomSheetState = RememberSheetState(skipHalfExpanded = true)”编辑为“val” BottomSheetState = RememberModalBottomSheetState(skipPartiallyExpanded = true)` (2认同)

Art*_*ian 12

更新 23/02/2023

从 Compose Material3 版本 1.1.0-alpha06 开始,ModalBottomSheet现已作为实验性可组合项提供(文档


我使用带有 AnimatedVisibility 的全屏对话框得到了非常相似的结果,如果感兴趣的话,这里是代码:

// Visibility state for the dialog which will trigger it only once when called
val transitionState = remember {
    MutableTransitionState(false).apply {
        targetState = true
    }
}

Dialog(
    onDismissRequest = {} // You can set a visibility state variable to false in here which will close the dialog when clicked outside its bounds, no reason to when full screen though,
    properties = DialogProperties(
        // This property makes the dialog full width of the screen
        usePlatformDefaultWidth = false
    )
) {

    // Visibility animation, more information in android docs if needed
    AnimatedVisibility(
        visibleState = transitionState,
        enter = slideInVertically(
            initialOffsetY = { it },
            animationSpec = ...
        ),
        exit = slideOutVertically(
            targetOffsetY = { it },
            animationSpec = ...
        )
    )
) {

    Box(
        modifier = Modifier.fillMaxSize().background(color = ...)
    ) {
        // Your layout

        // This can be any user interraction that closes the dialog
        Button(
            transitionState.apply { targetState = false }
        ) ...
    }
}
Run Code Online (Sandbox Code Playgroud)

所有这些都在一个可组合函数中,当执行打开所述对话框的 UI 操作时会调用该函数,这并不理想,但它可以工作。

希望我能帮上忙!


小智 6

Marlinski已经给出了一个很好的答案,但我想补充一点,还有一个ModalBottomSheetLayout,它也没有任何 Material 3 的实现。

我为现在需要它的人创建了一个要点: https ://gist.github.com/Pasha831/bdedcfee01acdc96cf3ae643da64f88a