🦐

【Flutter】フォーカス時に現れるボックスが角丸な入力欄を作成する

に公開

はじめに

DropdownButtonFormFieldTypeAheadFieldで、入力欄にフォーカスすると現れるドロップダウンリストやサジェストボックスを角丸にする方法を紹介します。
完成後の画面キャプチャは以下です。


ドロップダウンリスト

サジェストボックス

(Flutterバージョン)

> flutter --version
Flutter 3.16.5 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 78666c8dc5 (6 months ago) • 2023-12-19 16:14:14 -0800
Engine • revision 3f3e560236
Tools • Dart 3.2.3 • DevTools 2.28.4

角丸にする前

角丸にする前のコードと画面キャプチャを参考に載せておきます。

角丸にする前の実装
pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

+  flutter_typeahead: ^4.3.8
lib/main.dart
// ※GendersやPrefecturesはenumで定義してあります。
class _MyHomePageState extends State<MyHomePage> {
  Genders myGender = Genders.none;
  Prefectures myPrefecture = Prefectures.hokkaido;
  TextEditingController controller = TextEditingController();

  
  void initState() {
    super.initState();
    controller.text = myPrefecture.text;
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(25),
          child: Column(
            children: [
              DropdownButtonFormField<Genders>(
                decoration: const InputDecoration(
                  border: UnderlineInputBorder(),
                  labelText: '性別',
                ),
                items: [
                  for (final gender in Genders.values)
                    DropdownMenuItem(
                      value: gender,
                      child: Text(gender.text),
                    )
                ],
                onChanged: (value) {
                  if (value != null) {
                    setState(() {
                      myGender = value;
                    });
                  }
                },
              ),
              TypeAheadField(
                textFieldConfiguration: TextFieldConfiguration(
                  controller: controller,
                  decoration: const InputDecoration(
                    border: UnderlineInputBorder(),
                    labelText: '都道府県',
                  ),
                ),
                suggestionsCallback: (pattern) async {
                  List<Prefectures> matches = [...Prefectures.values];
                  matches
                      .retainWhere((element) => element.text.contains(pattern));
                  return matches;
                },
                itemBuilder: (context, suggestion) {
                  return ListTile(title: Text(suggestion.text));
                },
                onSuggestionSelected: (suggestion) => setState(() {
                  myPrefecture = suggestion;
                  controller.text = suggestion.text;
                }),
              ),
            ],
          ),
        ),
      ),
    );
  }
}


ドロップダウンリスト(角丸にする前)

サジェストボックス(角丸にする前)

結論から書くと、PopupMenuButton + InputDecoratorで作成しました。

PopupMenuButton(
  itemBuilder: (context) => [
    for (final gender in Genders.values)
      PopupMenuItem(
        value: gender,
        child: Text(gender.text),
      )
  ],
  initialValue: myGender,
  onSelected: (value) => setState(() {
    myGender = value;
  }),
  // 角丸なドロップダウンリストにする
  shape: const RoundedRectangleBorder(
    borderRadius: BorderRadius.all(Radius.circular(8.0))),
  child: InputDecorator(
    decoration: const InputDecoration(
      border: UnderlineInputBorder(),
      labelText: '性別',
    ),
    child: Text(myGender.text),
  ),
),

調べると、角丸なドロップダウンリストはPopupMenuButtonで実現するのが良さそうでした。↓
https://stackoverflow.com/questions/66135853/how-to-create-a-rounded-corner-of-dropdownbuttonformfield-flutter

ただ、入力欄に相当する部分をchildに指定しなければなりません。
自分は元のDropdownMenuButtonFormFieldに近い見た目にしたかったため、childInputDecoratorを用いることにしました。
このWidgetは見た目だけTextFieldのように見せてくれるようです。

TypeAheadFieldについて

TypeAheadFieldは、オプション引数suggestionsBoxDecorationを用いて簡単に角丸なサジェストボックスを作ることができます。

  TypeAheadField(
    ...
    // 角丸なサジェストボックスにする
+    suggestionsBoxDecoration: SuggestionsBoxDecoration(
+      borderRadius: BorderRadius.circular(8.0)),
  ),

まとめ

DropdownButtonFormFieldはPopupMenuButtonとInputDecoratorを組み合わせることで、TypeAheadFieldはオプション引数で指定してあげるだけで角丸なリストボックスを実現できます。
前者は少し工夫してそれらしい見た目にしましたが、もっとスマートな方法があるような気もします。。。

ともあれ欲しかったWidgetを作れたので満足です。

今回のコードは以下に載せてます。
https://github.com/dko5ki23t/flutter_zenn_samples/tree/main/rounded_dropdown

参考文献

https://stackoverflow.com/questions/66135853/how-to-create-a-rounded-corner-of-dropdownbuttonformfield-flutter
https://zenn.dev/hirotosuzuki/articles/bdc52850441b60

Discussion