Android Compose 显示底部对话框 (ModalBottomSheet),实现类似BottomSheetDialog的效果

1. ModalBottomSheet

ModalBottomSheetCompose官方的底部对话框,类似传统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")
    }
}

效果如下
在这里插入图片描述

2. 自定义圆角大小

通过定义ModalBottomSheetshape可以修改圆角的大小

shape = RoundedCornerShape(topStart = 3.dp, topEnd = 3.dp)

完整代码如下

//修改圆角
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun 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")
    }
}

效果如下
在这里插入图片描述

3. 去除拖动条

对话框顶部的拖动条,我们可能不需要,可以将其去除。
通过定义ModalBottomSheetdragHandle可以去除拖动条。

dragHandle = {
    
    }

完整代码如下

//去除拖动条
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun 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")
    }
}

效果如下
在这里插入图片描述

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")
    }
}

效果如下所示
在这里插入图片描述

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")
    }
}

效果如下所示
在这里插入图片描述

6. 返回键不隐藏弹框

将properties中的shouldDismissOnBackPress设置为false,返回键将不会关闭弹框。

properties = ModalBottomSheetProperties(shouldDismissOnBackPress = false)

7. 修改对话框的主体颜色

containerColor可以修改对话框的主体颜色,如果弹出的弹框底部横条颜色和对话框背景颜色不一样,可以通过修改containerColor来达到颜色一直的效果。

8. 更多ModalBottomSheet的使用

更多ModalBottomSheet的使用详见 : Android Developers | 底部动作条

9. BottomSheetScaffold

BottomSheetScaffold也是Compose官方自带的组件。提供更灵活的交互方式,底部弹窗可通过手势或代码控制展开和折叠,用户能在主界面和底部弹窗之间自由切换操作。
适用于需要频繁在主界面和底部弹窗之间交互的场景,如音乐播放器的播放列表,用户可一边浏览歌曲列表,一边操作主界面的播放控制按钮。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BottomSheetScaffoldExample() {
    
    
    val sheetState = rememberBottomSheetScaffoldState()
    BottomSheetScaffold(
        scaffoldState = sheetState,
        sheetContent = {
    
    
            Column(Modifier.height(300.dp)) {
    
    
                Text("这是底部弹窗内容")

            }
        },
        content = {
    
    
            Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
    
    
                Text("这是主内容区域")
            }
        }
    )
}

效果如下
在这里插入图片描述

10. 其他三方的Compose底部对话框库

10.1 bottomsheetdialog-compose

Github地址 : workspace/bottomsheetdialog-compose

Jetpack Compose BottomSheetDialog 库,允许您像使用 Dialog 的界面一样使用BottomsheetDialog。此外,它还支持在 BottomSheetDialog 显示时设置导航栏颜色。

在这里插入图片描述
这个看一下它的源码,可以发现内部调用的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
        }
    )
)
10.2 FlexibleBottomSheet

Github地址 : skydoves/FlexibleBottomSheet

FlexibleBottomSheet 是一种高级的 Compose Multiplatform 底部工作表,支持多平台,支持分段大小调整、最大的特点是弹出后,非弹框区域依旧能够操作,类似于高德地图的底部弹框。

在这里插入图片描述
解决WindowCompat.setDecorFitsSystemWindows(window, false)情况下,FlexibleBottomSheet弹框的bug

修改源码FlexibleBottomSheetPopupcontainSystemBars模式下,去除modifier.windowInsetsPadding(windowInsets),具体代码如下

@Composable
@InternalFlexibleApi
public actual fun FlexibleBottomSheetPopup(
  onDismissRequest: () -> Unit,
  windowInsets: WindowInsets,
  sheetState: FlexibleSheetState,
  content: @Composable BoxScope.() -> Unit,
) {
    
    
  val view = LocalView.current
  val id = rememberSaveable {
    
     UUID.randomUUID() }
  val parentComposition = rememberCompositionContext()
  val currentContent by rememberUpdatedState(content)
  val flexibleBottomSheetWindow = remember {
    
    

  val modifier = Modifier.semantics {
    
     this.popup() }
  if (!sheetState.containSystemBars){
    
    
    modifier.windowInsetsPadding(windowInsets)
  }

  FlexibleBottomSheetWindow(
    onDismissRequest = onDismissRequest,
    composeView = view,
    saveId = id,
    sheetState = sheetState,
  ).apply {
    
    
    setCustomContent(
      parent = parentComposition,
      content = {
    
    
        Box(
          modifier.imePadding(),
        ) {
    
    
          currentContent()
        }
      },
    )
  }
 }

 //省略其他代码...
}

11 参考

探索 Jetpack Compose 的底部抽屉:BottomSheetScaffold、ModalBottomSheetLayout 和 BackdropScaffold
手撸一个Compose三段式BottomSheetLayout

猜你喜欢

转载自blog.csdn.net/EthanCo/article/details/145039957
今日推荐