Jetpack Compose學(xué)習(xí)(16)——ModalBottomSheet(底部彈窗)
原文地址: Jetpack Compose學(xué)習(xí)(16)——ModalBottomSheet(底部彈窗)-Stars-One的雜貨小窩
接手新公司項(xiàng)目里,有代碼用到了這個(gè)彈窗,由于需要重構(gòu)架構(gòu)和進(jìn)行相關(guān)統(tǒng)一組件封裝,順手學(xué)習(xí)下這個(gè)組件,發(fā)現(xiàn)還是踩了些坑(怪我以Compose里的Dialog來用了哈哈)
介紹
這個(gè)組件是屬于M3里的組件,需要引入androidx.compose.material3這個(gè)依賴
不過新版本的Android Studio創(chuàng)建項(xiàng)目都是直接通過Bom引入了,是都會帶有了這個(gè)依賴了(這里就不過多提及加入依賴了)
PS:主要是現(xiàn)在新版本Android Studio,新創(chuàng)建的項(xiàng)目,依賴分了幾個(gè)文件,貼的話會很麻煩,見諒哈哈
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
基本使用
效果:

代碼:
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("內(nèi)容數(shù)據(jù)")
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")
}
}
}
}
}
這里需要注意的是:
- 展示彈窗,實(shí)際和Dialog類似,也是控制一個(gè)Boolean數(shù)值變化從而彈出
- 關(guān)閉彈窗的代碼得用上面的寫法,否則就是沒有下滑效果!!(之前就是在這踩坑了,雖然沒人關(guān)注這點(diǎn)哈哈)
PS:后續(xù)代碼為了保證重點(diǎn)和觀感,會有所精簡
ModalBottomSheet默認(rèn)是有半屏和全屏模式的,但上面例子由于我們的內(nèi)容元素不多,高度不是大,我們改造下,加個(gè)LazyColumn再看下效果
效果:

代碼:
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}")
}
}
}
}
自定義樣式
去掉小橫條
想要實(shí)現(xiàn)自定義的下拉樣式,然后,不希望要這個(gè)小橫條(如下圖所示),要如何實(shí)現(xiàn)呢?

可以通過dragHandle屬性來實(shí)現(xiàn),如下代碼
ModalBottomSheet(
onDismissRequest = { openBottomSheet = false },
sheetState = bottomSheetState,
dragHandle = {} //設(shè)置為空
) {
}
效果如下:

頂頭區(qū)自定義按鈕
這個(gè)dragHandle實(shí)際就是頂頭那篇區(qū)域,如果想要自定義一個(gè)取消和確定,也可以實(shí)現(xiàn),如下效果和代碼
效果:

代碼:
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
}
}
})
}
}
)
禁止展示半屏
如果不想要展示半屏的效果,可以通過下面這樣設(shè)置
val bottomSheetState = rememberModalBottomSheetState(true)
意思是,如果你的內(nèi)容滿全屏了,則直接跳過半屏模式,直接展示全屏,效果如下圖展示

那我內(nèi)容元素總高度不滿全屏高度,又該如何呢?
那更簡單,直接將你的內(nèi)容容器用Modifier設(shè)置為全屏即可
ModalBottomSheet(
onDismissRequest = { openBottomSheet = false },
sheetState = bottomSheetState,
) {
//直接填充滿屏即可!
Column(modifier= Modifier.fillMaxSize()) {
Text("內(nèi)容數(shù)據(jù)")
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")
}
}
}


浙公網(wǎng)安備 33010602011771號