Jetpack Compose学习(16)——ModalBottomSheet(底部弹窗)
<blockquote><p>原文地址: Jetpack Compose学习(16)——ModalBottomSheet(底部弹窗)-Stars-One的杂货小窝</p>
</blockquote>
<p>接手新公司项目里,有代码用到了这个弹窗,由于需要重构架构和进行相关统一组件封装,顺手学习下这个组件,发现还是踩了些坑(怪我以Compose里的Dialog来用了哈哈)</p>
<h2 id="介绍">介绍</h2>
<p>这个组件是属于M3里的组件,需要引入<code>androidx.compose.material3</code>这个依赖</p>
<p>不过新版本的Android Studio创建项目都是直接通过Bom引入了,是都会带有了这个依赖了(这里就不过多提及加入依赖了)</p>
<blockquote>
<p>PS:主要是现在新版本Android Studio,新创建的项目,依赖分了几个文件,贴的话会很麻烦,见谅哈哈</p>
</blockquote>
<pre><code>androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
</code></pre>
<h2 id="基本使用">基本使用</h2>
<p>效果:<br>
<img src="https://img2024.cnblogs.com/blog/1210268/202507/1210268-20250711180943746-19473053.gif" alt="PixPin_2025-07-11_18-08-42" loading="lazy"></p>
<p>代码:</p>
<pre><code class="language-kotlin">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")
}
}
}
}
}
</code></pre>
<p>这里需要注意的是:</p>
<ol>
<li>展示弹窗,实际和Dialog类似,也是控制一个Boolean数值变化从而弹出</li>
<li>关闭弹窗的代码得用上面的写法,否则就是没有下滑效果!!(之前就是在这踩坑了,虽然没人关注这点哈哈)</li>
</ol>
<blockquote>
<p>PS:后续代码为了保证重点和观感,会有所精简</p>
</blockquote>
<p><code>ModalBottomSheet</code>默认是有半屏和全屏模式的,但上面例子由于我们的内容元素不多,高度不是大,我们改造下,加个LazyColumn再看下效果</p>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/1210268/202507/1210268-20250711181747130-1580520299.gif" alt="PixPin_2025-07-11_18-17-34" loading="lazy"></p>
<p>代码:</p>
<pre><code class="language-kotlin">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}")
}
}
}
}
</code></pre>
<h2 id="自定义样式">自定义样式</h2>
<h3 id="去掉小横条">去掉小横条</h3>
<p>想要实现自定义的下拉样式,然后,不希望要这个小横条(如下图所示),要如何实现呢?<br>
<img src="https://img2024.cnblogs.com/blog/1210268/202507/1210268-20250711181901201-2120839342.png" alt="image" loading="lazy"></p>
<p>可以通过<code>dragHandle</code>属性来实现,如下代码</p>
<pre><code class="language-kotlin">ModalBottomSheet(
onDismissRequest = { openBottomSheet = false },
sheetState = bottomSheetState,
dragHandle = {} //设置为空
) {
}
</code></pre>
<p>效果如下:<br>
<img src="https://img2024.cnblogs.com/blog/1210268/202507/1210268-20250711182909163-104505158.png" alt="image" loading="lazy"></p>
<h3 id="顶头区自定义按钮">顶头区自定义按钮</h3>
<p>这个dragHandle实际就是顶头那篇区域,如果想要自定义一个取消和确定,也可以实现,如下效果和代码</p>
<p>效果:</p>
<p><img src="https://img2024.cnblogs.com/blog/1210268/202507/1210268-20250711183616082-1148455246.gif" alt="PixPin_2025-07-11_18-36-04" loading="lazy"></p>
<p>代码:</p>
<pre><code class="language-kotlin">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
}
}
})
}
}
)
</code></pre>
<h3 id="禁止展示半屏">禁止展示半屏</h3>
<p>如果不想要展示半屏的效果,可以通过下面这样设置</p>
<pre><code class="language-kotlin">val bottomSheetState = rememberModalBottomSheetState(true)
</code></pre>
<p>意思是,如果你的内容满全屏了,则直接跳过半屏模式,直接展示全屏,效果如下图展示</p>
<p><img src="https://img2024.cnblogs.com/blog/1210268/202507/1210268-20250711184232159-1485689212.gif" alt="PixPin_2025-07-11_18-42-27" loading="lazy"></p>
<p><strong>那我内容元素总高度不满全屏高度,又该如何呢?</strong></p>
<p>那更简单,直接将你的内容容器用Modifier设置为全屏即可</p>
<pre><code class="language-kotlin">ModalBottomSheet(
onDismissRequest = { openBottomSheet = false },
sheetState = bottomSheetState,
) {
//直接填充满屏即可!
Column(modifier= Modifier.fillMaxSize()) {
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")
}
}
}
</code></pre>
<h2 id="参考">参考</h2>
<ul>
<li>Bottom sheets | Jetpack Compose | Android Developers</li>
</ul>
</div>
<div id="MySignature" role="contentinfo">
<hr>
<span>提问之前,请先看</span>提问须知
<span>点击右侧图标发起提问</span>
<img border="0" src="http://wpa.qq.com/pa?p=2:1053894518:52" alt="联系我" title="联系我">
<span>或者加入QQ群一起学习</span>
<img border="0" src="//pub.idqqimg.com/wpa/images/group.png" alt="Stars-One安卓学习交流群" title="Stars-One安卓学习交流群">
TornadoFx学习交流群:1071184701
<img src="https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316120825333-1551152974.png" width="1000" height="auto">
<img src="https://img2018.cnblogs.com/blog/1210268/201905/1210268-20190508151523126-971809604.gif" width="1000" height="auto">
<!--<img src="https://img2020.cnblogs.com/blog/1210268/202004/1210268-20200413161422035-1188549898.gif" width="1000" height="auto">--><br><br>
来源:https://www.cnblogs.com/stars-one/p/18979413
頁:
[1]