Android Advent Calendar 2023の4日目の記事です。
昨年は、androidx.compose.material
の ModalBottomSheetLayout
に関する記事を投稿したのですが、今年は androidx.compose.material3
でも ModalBottomSheet
が提供されるようになりました。Material3になり、コンポーネントの使い方自体が ModalBottomSheetLayout
とは異なる形に変更されています。
昨年の記事: ModalBottomSheetLayoutの使い方とTips・トラブルシューティング
本記事では、昨年の記事の続きとして material3.ModalBottomSheet
の使い方やTips・トラブルシューティングについて紹介します。
※ 本記事の内容は、androidx.compose.material3:material3-*:1.2.0-alpha12
時点のものです。将来的に変更される可能性がありますのでご注意ください。
material.ModalBottomSheetLayoutとの差分
Material2 から Materail3への移行については下記マイグレーションガイドがあります。
ModalBottomSheetLayout
との差分として、コンポーネントの使い方やデザインのデフォルトが Material3 基準になっていることで変更点がいくつかあります。
デザイン
大きく下記の点が変更されており、コンポーネントのデフォルトの挙動が下記に従ったものに変更されています。
- カラーマッピングの変更とダイナミックカラーへの対応
- 角丸(28dp) が適用され、Material2 と比較して柔らかい印象に
- Sheet を Expand するときに利用する DragHandle が追加され、iOSの Sheets に近いデザインに
実装
material.ModalBottomSheetLayout
では、レイアウトの Composable をwrapして実装し、show・hideを呼び出す必要があったのに対し、 material3.ModalBottomSheet
ではコンポジションに入場した時だけ呼び出される形に変更されています。
Before (material.ModalBottomSheetLayout)
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
ModalBottomSheetLayout(
sheetState = sheetState,
sheetContent = {
// BottomSheet内に表示するコンテンツ
},
content = {
// BottomSheet自体を表示したい画面のコンテンツ
},
)
val coroutineScope = rememberCoroutineScope()
Button(
onClick = {
coroutineScope.launch {
sheetState.show()
}
}
) {
Text(text = "Button")
}
After (material3.ModalBottomSheet)
val sheetState = rememberModalBottomSheetState()
val showModal = remember { mutableStateOf(false) }
if (showModal.value) {
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = {
showModal.value = false
},
component = {
// BottomSheet内に表示するコンテンツ
},
)
}
Button(
onClick = {
showModal.value = true
}
) {
Text(text = "Button")
}
上記の例では、showModal.value
が true の場合のみコンポジションに入場し、 ModalBottomSheet
が描画されます。また、画面外をタップしたりスワイプによってシートを閉じた場合には onDismissRequest
が呼び出されるため、このタイミングで showModal.value
を false に戻すことで、シートを閉じることができます。
Tips・トラブルシューティング
ステータスバーの位置も暗転してほしい
昨年の記事では material.ModalBottomSheetLayout
でステータスバー領域が暗転されないことの解決として、WindowCompat.setDecorFitsSystemWindows(window, false)
+ AccompanistのSystemUiController を利用してedgeToEdge対応することで回避する方法を紹介しました。
Accompanist SystemUiController は現在非推奨になっており、 androidx.activity 1.8.0-alpha03
以降では、ComponentActivity.enableEdgeToEdge() を利用することが推奨されます。
EdgeToEdge未対応の場合
material3.ModalBottomSheet
ではこの問題が解決されており、edgeToEdge に対応していない場合、デフォルトでステータスバー領域まで暗転するようになっています。
EdgeToEdge対応済の場合
edgeToEdge が有効な場合、デフォルトではステータスバー領域が暗転しません。
edgeToEdge が有効な状態でも暗転させるようにするには、1.2.0-alpha02で追加された windowInsets
へ 0
もしくは 0.dp
を指定することで適用できます。
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
}
}
ModalBottomSheet(
sheetState = sheetState,
onDismissRequest = {
showModal.value = false
},
windowInsets = WindowInsets(0)
component = {
// BottomSheet内に表示するコンテンツ
},
)
コンテンツ領域にめり込んだ挙動になるのは変わりないので systemBarPadding()
などを適用し調整しましょう。
Material2相当のUIにしたい
material.ModalBottomSheetLayout
との差分の章でも紹介しましたが、デザイン面の変更に伴ってデフォルトのデザインが変更されています。 Material3 ライブラリには移行したいがデザインは変えたくないというケースがあると思います。この場合、下記のようにすることで Material2 相当のデザインとなります。
ModalBottomSheet(
shape = RoundedCornerShape(0.dp),
sheetState = modalBottomSheetState,
tonalElevation = 0.dp,
dragHandle = {},
onDismissRequest = {
showModal.value = false
},
) {
// BottomSheet内に表示するコンテンツ
}
変更点は下記の3つです。
- 角丸の削除
-
shape
を明示的に0dpを適用し角丸を無くします
-
-
tonalElevation
によるカラーの適用を行わない- Material3 では、
Surface
へtonalElevation
の高さに応じてPrimaryカラーから計算された色が標高として適用されます。ModalBottomSheet
内部ではSurface
が使われていることと、tonalElevation
はデフォルトでは 1dp が指定されていることで淡い色が自動的に適用されてしまいます。明示的に 0dp を指定することで適用されないようにすることができます
- Material3 では、
- dragHandleを削除
- dragHandleは
BottomSheetDefaults.DragHandle()
がデフォルトでは適用されますが、カスタマイズできるようになっており、空を渡すことで削除できます
- dragHandleは
IME表示時にTextFieldが上に飛んでいく
IME表示を行うとTextField位置がおかしくなる問題がありました。
alpha10で修正されたため、alpha10以降を利用することをおすすめします。
alpha10以前 | alpha10以降 |
---|---|
所感
2023年12月現在 material3-1.2.0 はまだ alpha12 ですが、 ModalBottomSheet 関連のバグが高頻度で修正されていますので、利用される場合には stable の 1.1.2 ではなく、最新を利用する方が良いでしょう。
昨年の所感で material.ModalBottomSheetLayout
について複雑性が高く使いづらいと記載しましたが、 material3.ModalBottomSheet
では、その問題がかなり解決されているように感じました。
レイアウトでwrapする必要がなくなったことで、複数の画面で同一のUIを利用したい場合にも Composable Function の共通化がしやすくなったと思います。