火影 发表于 2025-1-13 07:31:11

Android Compose 显示底部对话框 (ModalBottomSheet),实现雷同BottomSheet

1. ModalBottomSheet

ModalBottomSheet是Compose官方的底部对话框,雷同传统View中的BottomSheetDialog,可以实现从底部弹出,并支持滑动关闭的效果。
必要注意的是,这里使用的是material3,然后ModalBottomSheet这个组件目前还是实验性的,未来大概会改变或删除。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyModalBottomSheetTest1() {
    val sheetState = rememberModalBottomSheetState()
    val scope = rememberCoroutineScope()
    var showBottomSheet by remember { mutableStateOf(false) }

    if (showBottomSheet) {
      ModalBottomSheet(
            onDismissRequest = {
                showBottomSheet = false
            },
            sheetState = sheetState
      ) {
            // Sheet content
            Column(Modifier.height(300.dp)) {
                Button(onClick = {
                  scope.launch { sheetState.hide() }.invokeOnCompletion {
                        if (!sheetState.isVisible) {
                            showBottomSheet = false
                        }
                  }
                }) {
                  Text("Hide bottom sheet")
                }
            }
      }
    }

    Button(onClick = {
      showBottomSheet = true
    }, Modifier.padding(50.dp)) {
      Text("show")
    }
}
效果如下
https://i-blog.csdnimg.cn/direct/34f8d0b247934b13b6c00cbcd01b4fce.jpeg
2. 自定义圆角大小

通过定义ModalBottomSheet的shape可以修改圆角的大小
shape = RoundedCornerShape(topStart = 3.dp, topEnd = 3.dp)
完整代码如下
//修改圆角@OptIn(ExperimentalMaterial3Api::class)@Composablefun MyModalBottomSheetTest2() {    val sheetState = rememberModalBottomSheetState()    val scope = rememberCoroutineScope()    var showBottomSheet by remember { mutableStateOf(false) }    if (showBottomSheet) {      ModalBottomSheet(            shape = RoundedCornerShape(topStart = 3.dp, topEnd = 3.dp)
,            onDismissRequest = {                showBottomSheet = false            },            sheetState = sheetState      ) {            // Sheet content            Column(Modifier.height(300.dp)) {                Button(onClick = {                  scope.launch { sheetState.hide() }.invokeOnCompletion {                        if (!sheetState.isVisible) {                            showBottomSheet = false                        }                  }                }) {                  Text("Hide bottom sheet")                }            }      }    }    Button(onClick = {      showBottomSheet = true    }, Modifier.padding(50.dp)) {      Text("show")    }} 效果如下
https://i-blog.csdnimg.cn/direct/cb429d4c72ed4e46b4cfc403a8e4a4a8.jpeg
3. 去除拖动条

对话框顶部的拖动条,我们大概不必要,可以将其去除。
通过定义ModalBottomSheet的dragHandle可以去除拖动条。
dragHandle = {}
完整代码如下
//去除拖动条@OptIn(ExperimentalMaterial3Api::class)@Composablefun MyModalBottomSheetTest3() {    val sheetState = rememberModalBottomSheetState()    val scope = rememberCoroutineScope()    var showBottomSheet by remember { mutableStateOf(false) }    if (showBottomSheet) {      ModalBottomSheet(            dragHandle = {}
,            onDismissRequest = {                showBottomSheet = false            },            sheetState = sheetState      ) {            // Sheet content            Column(Modifier.height(300.dp)) {                Button(onClick = {                  scope.launch { sheetState.hide() }.invokeOnCompletion {                        if (!sheetState.isVisible) {                            showBottomSheet = false                        }                  }                }) {                  Text("Hide bottom sheet")                }            }      }    }    Button(onClick = {      showBottomSheet = true    }, Modifier.padding(50.dp)) {      Text("show")    }} 效果如下
https://i-blog.csdnimg.cn/direct/03af8b2191604863bc362e8763f17d46.jpeg
4. 默认半屏,滑动变成全屏

这里我们把Column的高度改为800.dp,运行步伐,对话框默认以半屏显示,向上滑动对话框,可以发现,对话框会变为全屏。
//半屏展开
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyModalBottomSheetTest4() {
    val sheetState = rememberModalBottomSheetState(
      skipPartiallyExpanded = false,
    )
    val scope = rememberCoroutineScope()
    var showBottomSheet by remember { mutableStateOf(false) }

    if (showBottomSheet) {
      ModalBottomSheet(
            onDismissRequest = {
                showBottomSheet = false
            },
            sheetState = sheetState
      ) {
            // Sheet content
            Column(Modifier.height(800.dp)) {
                Button(onClick = {
                  scope.launch { sheetState.hide() }.invokeOnCompletion {
                        if (!sheetState.isVisible) {
                            showBottomSheet = false
                        }
                  }
                }) {
                  Text("Hide bottom sheet")
                }
            }
      }
    }

    Button(onClick = {
      showBottomSheet = true
    }, Modifier.padding(50.dp)) {
      Text("show")
    }
}
效果如下所示
https://i-blog.csdnimg.cn/direct/1fddc34969bf4fb39aa5dba6dff7e13b.gif#pic_center
5. 默认全屏

将skipPartiallyExpanded修改为true,打开对话框的时间,会立即以全屏展示,向下滑动采取的时间,也会跳过半屏阶段。
//全屏展开
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MyModalBottomSheetTest5() {
    val sheetState = rememberModalBottomSheetState(
      skipPartiallyExpanded = true,
    )
    val scope = rememberCoroutineScope()
    var showBottomSheet by remember { mutableStateOf(false) }

    if (showBottomSheet) {
      ModalBottomSheet(
            onDismissRequest = {
                showBottomSheet = false
            },
            sheetState = sheetState
      ) {
            // Sheet content
            Column(Modifier.height(800.dp)) {
                Button(onClick = {
                  scope.launch { sheetState.hide() }.invokeOnCompletion {
                        if (!sheetState.isVisible) {
                            showBottomSheet = false
                        }
                  }
                }) {
                  Text("Hide bottom sheet")
                }
            }
      }
    }

    Button(onClick = {
      showBottomSheet = true
    }, Modifier.padding(50.dp)) {
      Text("show")
    }
}
效果如下所示
https://i-blog.csdnimg.cn/direct/17c79faeb076495d8387cf2fb53f0798.gif#pic_center
6. 返回键不隐藏弹框

将properties中的shouldDismissOnBackPress设置为false,返回键将不会关闭弹框。
properties = ModalBottomSheetProperties(shouldDismissOnBackPress = false)
7. 修改对话框的主体颜色

containerColor可以修改对话框的主体颜色,如果弹出的弹框底部横条颜色和对话框配景颜色不一样,可以通过修改containerColor来到达颜色一直的效果。
8. 更多ModalBottomSheet的使用

更多ModalBottomSheet的使用详见 : Android Developers | 底部动作条
9.其他三方的Compose底部对话框库

9.1 bottomsheetdialog-compose

Github地点 : workspace/bottomsheetdialog-compose
Jetpack Compose BottomSheetDialog 库,允许您像使用 Dialog 的界面一样使用BottomsheetDialog。别的,它还支持在 BottomSheetDialog 显示时设置导航栏颜色。
https://i-blog.csdnimg.cn/direct/9503df9c7afc4b239f0c1abb36baa5b5.png
这个看一下它的源码,可以发现内部调用的BottomSheetDialogWrapper,而BottomSheetDialogWrapper继承自com.google.android.material.bottomsheet.BottomSheetDialog,也就是说,bottomsheetdialog-compose这个库实质上是封装了传统View中的BottomSheetDialog,来提供给Compose使用。
private class BottomSheetDialogWrapper(
    private var onDismissRequest: () -> Unit,
    private var properties: BottomSheetDialogProperties,
    private val composeView: View,
    layoutDirection: LayoutDirection,
    density: Density,
    dialogId: UUID
) : BottomSheetDialog(
    ContextThemeWrapper(
      composeView.context,
      if (properties.enableEdgeToEdge) {
            R.style.TransparentEdgeToEdgeEnabledBottomSheetTheme
      } else {
            R.style.TransparentEdgeToEdgeDisabledBottomSheetTheme
      }
    )
)
9.2 FlexibleBottomSheet

Github地点 : skydoves/FlexibleBottomSheet
FlexibleBottomSheet 是一种高级的 Compose Multiplatform 底部工作表,用于分段大小调整、非模态类型,并允许在底部工作表背面举行交互,雷同于 Google Maps。它还提供了其他便利,包括指定工作表大小、监视工作表状态和更多自定义。
https://i-blog.csdnimg.cn/direct/f3bfa50017224fde95b2a5b851cb1e45.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Android Compose 显示底部对话框 (ModalBottomSheet),实现雷同BottomSheet