LoginSignup
3
0

material3.ModalBottomSheetの使い方とTips・トラブルシューティング

Posted at

Android Advent Calendar 2023の4日目の記事です。

昨年は、androidx.compose.materialModalBottomSheetLayout に関する記事を投稿したのですが、今年は 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 に近いデザインに

スクリーンショット 0005-12-04 7.17.54.png

実装

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で追加された windowInsets0 もしくは 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 では、 SurfacetonalElevation の高さに応じてPrimaryカラーから計算された色が標高として適用されます。 ModalBottomSheet 内部では Surface が使われていることと、 tonalElevation はデフォルトでは 1dp が指定されていることで淡い色が自動的に適用されてしまいます。明示的に 0dp を指定することで適用されないようにすることができます
  • dragHandleを削除
    • dragHandleは BottomSheetDefaults.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 の共通化がしやすくなったと思います。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0