Jetpack Compose学习(16)——ModalBottomSheet(底部弹窗)

210 阅读4分钟

本篇大约有5564个字,阅读预计需要6.96分钟

合集 - Android Jetpack Compose(17) 1. Jetpack Compose学习(1)——从登录页开始入门 2021-08-11 2. Jetpack Compose学习(2)——文本(Text)的使用 2021-08-22 3. Jetpack Compose学习(3)——图标(Icon) 按钮(Button) 输入框(TextField) 的使用 2021-09-08 4. Jetpack Compose学习(4)——Image(图片)使用及Coil图片异步加载库使用 2021-09-15 5. Jetpack Compose学习(5)——从登录页美化开始学习布局组件使用 2021-09-25 6. Jetpack Compose学习(6)——关于Modifier的妙用 2021-10-03 7. Jetpack Compose学习(7)——MD样式架构组件Scaffold及导航底部菜单 2021-10-04 8. Jetpack Compose学习(8)——State及remeber 2022-07-30 9. Jetpack Compose学习(9)——Compose中的列表控件(LazyRow和LazyColumn) 2022-08-11 10. Jetpack Compose学习(10)——使用Compose物料清单BOM,更好管理依赖版本 2023-02-16 11. Jetpack Compose学习(11)——Navigation页面导航的使用 2023-02-25 12. Jetpack Compose学习(12)——Material Theme的主题色切换 2024-07-26 13. Jetpack Compose学习(13)——Compose生命周期及副作用函数 2024-08-15 14. Jetpack Compose学习(14)——ConstraintLayout约束布局使用 2024-12-05 15. Jetpack Compose学习(15)——Pager组件的使用(对标ViewPager) 2024-12-18 16. Compose里Navigation导航的优雅跳转 04-24 17. Jetpack Compose学习(16)——ModalBottomSheet(底部弹窗) 07-11 收起 原文地址: Jetpack Compose学习(16)——ModalBottomSheet(底部弹窗)-Stars-One的杂货小窝

接手新公司项目里,有代码用到了这个弹窗,由于需要重构架构和进行相关统一组件封装,顺手学习下这个组件,发现还是踩了些坑(怪我以Compose里的Dialog来用了哈哈)

介绍 这个组件是属于M3里的组件,需要引入androidx.compose.material3这个依赖

不过新版本的Android Studio创建项目都是直接通过Bom引入了,是都会带有了这个依赖了(这里就不过多提及加入依赖了)

PS:主要是现在新版本Android Studio,新创建的项目,依赖分了几个文件,贴的话会很麻烦,见谅哈哈

androidx-material3 = { group = www.hefeilaws.com/ "androidx.compose.material3", name = "material3" } 基本使用 效果: PixPin_2025-07-11_18-08-42

代码:

import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.material3.Button import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import kotlinx.coroutines.launch

@OptIn(ExperimentalMaterial3Api::class) @Composable fun ModelSheetDemoPage(modifier: Modifier = Modifier) { val context = LocalContext.current

Column(modifier = Modifier.statusBarsPadding()) {

    var openBottomSheet by rememberSaveable { mutableStateOf(false) }
    val scope = rememberCoroutineScope()
    val bottomSheetState = rememberModalBottomSheetState()

    // App content
    Column(
        horizontalAlignment = Alignment.Start,
        verticalArrangement = Arrangement.spacedBy(4.dp)
    ) {

        Button(
            onClick = { openBottomSheet = !openBottomSheet },
            modifier = Modifier.align(Alignment.CenterHorizontally)
        ) {
            Text(text = "Show Bottom Sheet")
        }
    }

    // Sheet content
    if (openBottomSheet) {
        ModalBottomSheet(
            onDismissRequest = { openBottomSheet = false },
            sheetState = bottomSheetState,
        ) {

            Text("内容数据")

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

        }
    }
}

}

这里需要注意的是:

展示弹窗,实际和Dialog类似,也是控制一个Boolean数值变化从而弹出 关闭弹窗的代码得用上面的写法,否则就是没有下滑效果!!(之前就是在这踩坑了,虽然没人关注这点哈哈) PS:后续代码为了保证重点和观感,会有所精简

ModalBottomSheet默认是有半屏和全屏模式的,但上面例子由于我们的内容元素不多,高度不是大,我们改造下,加个LazyColumn再看下效果

效果:

PixPin_2025-07-11_18-17-34

代码:

ModalBottomSheet( onDismissRequest = { openBottomSheet = false }, sheetState = bottomSheetState, ) {

//....

LazyColumn {
	items(20) {
		Row(modifier= Modifier.fillMaxWidth().height(48.dp).padding(horizontal = 24.dp), verticalAlignment = Alignment.CenterVertically) {
			Text("hello ${it}")
		}
	}
}

} 自定义样式 去掉小横条 想要实现自定义的下拉样式,然后,不希望要这个小横条(如下图所示),要如何实现呢? image

可以通过dragHandle属性来实现,如下代码

ModalBottomSheet( onDismissRequest = { openBottomSheet = false }, sheetState = bottomSheetState, dragHandle = {www.hefeilaws.com/} //设置为空 ) {

} 效果如下: image

顶头区自定义按钮 这个dragHandle实际就是顶头那篇区域,如果想要自定义一个取消和确定,也可以实现,如下效果和代码

效果:

PixPin_2025-07-11_18-36-04

代码:

ModalBottomSheet( onDismissRequest = { openBottomSheet = false }, sheetState = bottomSheetState, dragHandle = { Row(modifier= Modifier.fillMaxWidth().padding(horizontal = 24.dp)) { Text("取消",modifier= Modifier.clickable{ scope .launch { bottomSheetState.hide() } .invokeOnCompletion { if (!bottomSheetState.isVisible) { openBottomSheet = false } } }) Spacer(modifier= Modifier.weight(1f)) Text("确定",modifier= Modifier.clickable{ scope .launch { bottomSheetState.hide() } .invokeOnCompletion { if (!bottomSheetState.isVisible) { openBottomSheet = false } } }) } } ) 禁止展示半屏 如果不想要展示半屏的效果,可以通过下面这样设置

val bottomSheetState = rememberModalBottomSheetState(true) 意思是,如果你的内容满全屏了,则直接跳过半屏模式,直接展示全屏,效果如下图展示

PixPin_2025-07-11_18-42-27

那我内容元素总高度不满全屏高度,又该如何呢?

那更简单,直接将你的内容容器用Modifier设置为全屏即可

ModalBottomSheet( onDismissRequest = { openBottomSheet = false }, sheetState = bottomSheetState, ) {

//直接填充满屏即可!
Column(modifier= Modifier.fillMaxSize()) {
	Text("内容数据")

	Button(https://www.hefeilaws.com/
		// 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")
	}
}

}