在 Jetpack Compose 中,我有一个包含一些内容的
BottomSheetScaffold
。该内容是从 ViewModel 中观察到的,如下所示:
BottomSheetScaffold(
sheetContent = { SheetContent(sheetData = viewModel.sheetData) }
) {}
因此,每当
viewModel.sheetData
发生变化时,就会触发重组。每当发生这种情况时,底部工作表就会自动展开。这是一个错误还是一个功能?我可以禁用这个吗?我正在使用最新版本:1.1.0-alpha01
编辑:这是一个使用
LaunchedEffect
而不是 ViewModel 的示例。
@OptIn(ExperimentalMaterialApi::class)
@Preview
@Composable
fun HomeScreen() {
var addSheetData by remember { mutableStateOf(false) }
LaunchedEffect(true) {
delay(2000)
addSheetData = true
}
BottomSheetScaffold(sheetContent = {
if (addSheetData) {
Column {
Text(text = "Text1", fontSize = 36.sp)
Text(text = "Text2", fontSize = 36.sp)
Text(text = "Text3", fontSize = 36.sp)
Text(text = "Text4", fontSize = 36.sp)
Text(text = "Text5", fontSize = 36.sp)
}
}
}, sheetBackgroundColor = Color.LightGray) {}
}
包含 5 个文本的工作表会自动展开。
这似乎是一个错误。
目前(
1.0.x
和 1.1.0-alpha01
)当 sheetContentHeight
< sheetPeekHeight
时,就会发生这种情况。scaffoldState.bottomSheetState
结果 expanded
。
您可以在代码中轻松验证它:
sheetBackgroundColor = if (scaffoldState.bottomSheetState.isCollapsed) Color.LightGray else Color.Yellow
当
addSheetData == false
时,背景颜色变为 Yellow
。当您重新组合可组合项时,由于状态已展开,因此内容表已完全展开。
作为解决方法,您可以使用类似以下内容:
sheetContent = {
if (!addSheetData){
Box(Modifier.fillMaxWidth().height(60.dp))
}
if (addSheetData) {
Column {
Text(text = "Text1", fontSize = 36.sp)
Text(text = "Text2", fontSize = 36.sp)
Text(text = "Text3", fontSize = 36.sp)
Text(text = "Text4", fontSize = 36.sp)
Text(text = "Text5", fontSize = 36.sp)
}
}
}
尝试添加初始值:
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = BottomSheetState(
initialValue = BottomSheetValue.Collapsed,
)
)
我设法通过创建一个管理脚手架状态的自定义状态来解决这个问题,并根据我的自定义状态显示工作表内容,因为脚手架状态始终返回到
Expanded
。
这是我的解决方案:
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun CustomBottomSheet(
customBottomSheetState: CustomBottomSheetState = rememberCustomBottomSheetState(remember { CustomBottomSheetState() }),
topBar: (@Composable () -> Unit)? = null,
sheetContent: @Composable() (ColumnScope.() -> Unit),
content: @Composable () -> Unit
) {
val scaffoldState: BottomSheetScaffoldState = rememberBottomSheetScaffoldState()
customBottomSheetState.scaffoldState = scaffoldState
// this to handle updating the state when collapsing by swiping the sheet down
if (scaffoldState.bottomSheetState.isCollapsed) {
customBottomSheetState.currentValue.value = CustomBottomSheetValue.Collapsed
}
BottomSheetScaffold(
sheetPeekHeight = 0.dp,
topBar = topBar,
scaffoldState = scaffoldState,
sheetShape = BottomSheetShape,
sheetContent = {
if (customBottomSheetState.isExpanded()) {
sheetContent()
}
}
) {
Box(Modifier.padding(it)) {
content()
}
}
}
@Stable
@OptIn(ExperimentalMaterialApi::class)
class CustomBottomSheetState constructor(
initialValue: MutableState<CustomBottomSheetValue> = mutableStateOf(CustomBottomSheetValue.Collapsed)
) {
var currentValue = initialValue
var scaffoldState: BottomSheetScaffoldState? = null
suspend fun expand() {
scaffoldState?.bottomSheetState?.expand()
currentValue.value = CustomBottomSheetValue.Expanded
}
suspend fun collapse() {
scaffoldState?.bottomSheetState?.collapse()
currentValue.value = CustomBottomSheetValue.Collapsed
}
fun isCollapsed(): Boolean {
return this.currentValue.value == CustomBottomSheetValue.Collapsed
}
fun isExpanded(): Boolean {
return this.currentValue.value == CustomBottomSheetValue.Expanded
}
}
enum class CustomBottomSheetValue {
Expanded, Collapsed;
}
@Composable
fun rememberCustomBottomSheetState(bottomSheetState: CustomBottomSheetState = CustomBottomSheetState()) =
remember(bottomSheetState) { bottomSheetState }
这就是它的用法:
val bottomSheetState = rememberCustomBottomSheetState()
CustomBottomSheet(topBar = {
// topbar
}, customBottomSheetState = bottomSheetState, sheetContent = {
// sheet
}) {
// screen
}
您可以修改参数以包含所有
BottomSheetScaffold
参数。
我无法修复不需要的重组。发生的事情是每当它重新组合时,它就会回到
initialValue: SheetValue.
就我而言,我使用的是 rememberStandardBottomSheetState
,它的默认值为 initialValue: SheetValue = PartiallyExpanded
,所以每次它都会重新组合我的 BottomSheet“最小化”...
我的解决方法是将初始值设置为可保存状态,并在状态发生变化时更新它,以便在重新组合时,bottomSheet 保持其状态。
var sheetState by remember { mutableStateOf(SheetValue.PartiallyExpanded) }
如果您的bottomSheet只能通过“拖动”进行更改,您可以通过在
confirmValueChange
中找到的rememberStandardBottomSheetState
拦截状态更改,在我的情况下,我还可以通过dragHandle onClicks展开()/“最小化”()。您可以从我的代码中获得启发:
@Composable
private fun StickyBottomSheet(
content: @Composable ColumnScope.() -> Unit,
title: String? = null,
onStateChange: (BottomSheetState) -> Unit = {}
){
val scope = rememberCoroutineScope()
var sheetState by remember { mutableStateOf(SheetValue.PartiallyExpanded) }
val state = rememberStandardBottomSheetState(
initialValue = sheetState,
confirmValueChange = { sheetValue ->
sheetState = sheetValue
when (sheetValue) {
SheetValue.Hidden -> false
SheetValue.Expanded -> {
onStateChange(BottomSheetState.Expanded)
true
}
SheetValue.PartiallyExpanded -> {
onStateChange(BottomSheetState.Minimised)
true
}
}
}
)
val scaffoldState = rememberBottomSheetScaffoldState(bottomSheetState = state)
BottomSheetScaffold(
sheetContent = content,
scaffoldState = scaffoldState,
sheetShadowElevation = 8.dp,
sheetDragHandle = {
DragHandle(
title = title,
onClick = {
scope.launch {
if (scaffoldState.bottomSheetState.currentValue == SheetValue.PartiallyExpanded) {
sheetState = SheetValue.Expanded
scaffoldState.bottomSheetState.expand()
onStateChange(BottomSheetState.Expanded)
} else {
scaffoldState.bottomSheetState.show()
onStateChange(BottomSheetState.Dismissed)
}
}
},
expanded = scaffoldState.bottomSheetState.currentValue == SheetValue.Expanded
)
}
) {
}
}
@Composable
private fun DragHandle(title: String?, onClick: () -> Unit = {}, expanded: Boolean){
Surface(
modifier = Modifier.height(56.dp)
) {
AnimatedVisibility(visible = expanded, enter = fadeIn(), exit = fadeOut()) {
Box(modifier = Modifier.fillMaxWidth()) {
Box(
Modifier
.padding(top = 16.dp)
.size(width = 32.dp, height = 4.dp)
.background(wjColorScheme.outline)
.align(Alignment.TopCenter)
)
IconButton(
modifier = Modifier.align(Alignment.CenterEnd),
onClick = { onClick() },
content = { Icon(R.drawable.ic_close_24 )})
}
}
AnimatedVisibility(!expanded, enter = fadeIn(), exit = fadeOut()) {
Row(modifier = Modifier.clickable { onClick() }
) {
ListItem(
leadingContent = { Icon(R.drawable.ic_add_24) },
text2 = title
)
}
}
}
}