BottomSheetScaffold
を使って BottomSheet を作るとき、BottomSheet の中身は BottomSheetScaffold
の引数にある sheetContent
で作っていきます。
BottomSheetScaffold( sheetContent = { /* BottomSheet の中身 */ } ) { paddingValues -> /* 画面の中身 */ }
この画面において BottomSheet の表示に複数の種類がある場合を考えてみます。
種類に応じて sheetContent
を切り替えたいので、次のような enum による分岐をしてみます。
今回は 2 種類の BottomSheet を一つの画面で表示する想定で BottomSheetType
という enum を定義して sheetContent
内部で表示を切り替えます。
このとき、BottomSheet には開いた状態と閉じた状態の 2 種類があることを考慮に入れ、閉じた場合に sheetContent
を表示しないようにするためなにもないことを示す値も合わせて定義してみます。
enum class BottomSheetType { NONE, FOO, BAR } var sheetType by remember { mutableStateOf(BottomSheetType.NONE) } val bottomSheetState = rememberBottomSheetState( initialValue = BottomSheetValue.Collapsed, ) val scaffoldState = rememberBottomSheetScaffoldState( bottomSheetState = bottomSheetState, ) BottomSheetScaffold( scaffoldState = scaffoldState, sheetPeekHeight = 0.dp, sheetContent = { when (sheetType) { BottomSheetType.NONE -> { // 閉じた状態を表現したい。何も表示するものはないので空のまま。 } BottomSheetType.FOO -> { Text(text = "foo") } BottomSheetType.BAR -> { Text(text = "bar") } } } )
このようなコードで sheetContent
を作る画面を動かすと BottomSheetState
のもつ状態がおかしくなってしまいます。具体的には、rememberBottomSheetState
で初期値を Collapsed
にしていても、Composition がおわると Expanded
な状態であると判定されてしまいます。
またこのような sheetContent
の作り方を実装した上で BottomSheet を展開しようと次のようなコードを呼び出すとアプリがクラッシュします。
// Jetpack Compose 1.1.0 でクラッシュするコード (1.2.0 ではクラッシュしない) coroutineScope.launch { bottomSheetState.expand() } // Jetpack Compose 1.2.0 でクラッシュするコード (1.1.0 ではクラッシュしない) coroutineScope.launch { bottomSheetState.animateTo(BottomSheetValue.Collapsed) }
クラッシュ時の例外は次の通りで、展開時のアニメーションを実行しようとしたとき、アニメーションが終了する位置が指定できていないことでクラッシュしていることがわかります。
java.lang.IllegalArgumentException: The target value must have an associated anchor.
そもそも BottomSheet を展開していないのに BottomSheetState
は Expanded
になってしまうことが良くないのですが、これは sheetContent
で何も Composable を呼ばないケースが不具合を引き起こしています。
このため、次のように何も表示しないケースで Spacer(modifier = Modifier.height(1.dp))
を差し込むか、when
全体を Box
で囲むか、そもそも enum の定義から表示しないパターンの定義を消すかのいずれかで、常に sheetContent
が何かしらの Composable を呼び出すようにする必要があります。
// 小さな Spacer を差し込むパターン BottomSheetScaffold( scaffoldState = scaffoldState, sheetPeekHeight = 0.dp, sheetContent = { when (sheetType) { BottomSheetType.NONE -> { Spacer(modifier = Modifier.height(1.dp)) } BottomSheetType.FOO -> { Text(text = "foo") } BottomSheetType.BAR -> { Text(text = "bar") } } } ) // when 全体を Box で囲むパターン。BottomSheetType.NONE でも最低 1dp は確保する BottomSheetScaffold( scaffoldState = scaffoldState, sheetPeekHeight = 0.dp, sheetContent = { // 最低 1dp の高さは確保 Box(modifier = Modifier.requiredHeightIn(min = 1.dp)) { when (sheetType) { BottomSheetType.NONE -> { // 閉じた状態を表現したい。何も表示するものはないので空のまま。 } BottomSheetType.FOO -> { Text(text = "foo") } BottomSheetType.BAR -> { Text(text = "bar") } } } } ) // enum の定義を削除するパターン BottomSheetScaffold( scaffoldState = scaffoldState, sheetPeekHeight = 0.dp, sheetContent = { when (sheetType) { BottomSheetType.FOO -> { Text(text = "foo") } BottomSheetType.BAR -> { Text(text = "bar") } } } )