Skip to content
sho10case
Go back

Jetpack Composeでドロップダウンメニュー(ExposedDropdownMenuBox)を実装する完全ガイド【Material 3】

Android アプリ開発において、複数の選択肢から一つを選ばせる「ドロップダウンメニュー」は頻出の UI 要素です。

Jetpack Compose(特に Material 3)では、ExposedDropdownMenuBox を使用するのが標準的な実装方法ですが、従来の Spinner とは考え方が異なり、初めて触る際に「メニューが表示されない」「選択した値が反映されない」といった罠にハマりやすいポイントでもあります。

今回は、逆引きリファレンスとしてそのまま使える、最も標準的かつ実戦的な実装パターンを解説します。


1. 基本の実装コード(コピペ用)

Material 3 のコンポーネントを使用した、最も標準的な実装例です。

@OptIn(ExperimentalMaterial3Api::class)
@Composable
def ExposedDropdownMenuSample() {
    val options = listOf("オプション1", "オプション2", "オプション3", "オプション4")
    var expanded by remember { mutableStateOf(false) }
    var selectedOptionText by remember { mutableStateOf(options[0]) }

    // 外側のコンテナ
    ExposedDropdownMenuBox(
        expanded = expanded,
        onExpandedChange = { expanded = !expanded },
        modifier = Modifier.fillMaxWidth().padding(16.dp)
    ) {
        // 入力フィールド(readOnly = true にすることでタップ専用にする)
        TextField(
            modifier = Modifier.menuAnchor(), // ここが重要!
            readOnly = true,
            value = selectedOptionText,
            onValueChange = {},
            label = { Text("ラベル") },
            trailingIcon = {
                ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded)
            },
            colors = ExposedDropdownMenuDefaults.textFieldColors(),
        )

        // 実際のドロップダウンメニュー部分
        ExposedDropdownMenu(
            expanded = expanded,
            onDismissRequest = { expanded = false },
        ) {
            options.forEach { selectionOption ->
                DropdownMenuItem(
                    text = { Text(selectionOption) },
                    onClick = {
                        selectedOptionText = selectionOption
                        expanded = false
                    },
                    contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
                )
            }
        }
    }
}

2. 実装の重要ポイント

Modifier.menuAnchor()

Material 3 から導入された非常に重要な Modifier です。これを TextField(またはアンカーとなる要素)に付与することで、メニューを表示する位置をシステムに正しく伝えます。これを忘れると、メニューが変な場所に表示されたり、表示されなかったりします。

readOnly = true

ドロップダウンとして使う場合、キーボード入力は不要なケースが多いです。readOnly = true に設定することで、要素全体をタップした際にメニューが開く挙動になります。

ExposedDropdownMenuDefaults.TrailingIcon

メニューが開いているか閉じているかに合わせて、自動で矢印の向きが変わるアイコンを提供してくれます。


3. よくあるカスタマイズ

検索機能付きドロップダウン(Editable)

ユーザーが文字を入力して選択肢を絞り込めるようにするには、readOnly = false にし、onValueChange でリストをフィルタリングするロジックを追加します。

エラー状態の表示

TextFieldisError = true プロパティと連動させることで、未選択時のバリデーションなどを視覚的に表現できます。


4. まとめ

ExposedDropdownMenuBox は、Material 3 のガイドラインに沿った美しい UI を簡単に提供してくれます。

  1. State で expanded を管理する
  2. menuAnchor() を忘れずに付ける
  3. DropdownMenuItem で値を更新する

この3点を押さえておけば、Compose でのメニュー実装に迷うことはありません。

さらに詳しいカスタマイズや公式ドキュメントは、Android Developers 公式サイト(英語)も併せて参照してください。


あわせて読みたい記事:



Related Posts