Flutter:Autocompleteの候補リストを入力欄の上方に表示させる方法

※当サイトは、アフィリエイト広告を利用しています

how to備忘録

「TextField」や「TextFormField」に、入力候補を表示してくれる「Autocomplete」は、とても便利で、かつ外部パッケージではなく、Flutter標準のクラスなので、よく使っています。

 

しかし、テキスト入力欄が画面の下の方にある場合を想定して、入力候補の表示を、入力欄の下方ではなく上方に表示させようとしたところ、思いのほか、苦労しました。

 

ネット情報やChat-GPTを活用しても、スムーズには解決できなかったので、その過程を簡単に共有します。

 

前提とする環境

  • PC:MacBook Pro(Intel Core i5)
  • OS:macOS Sonoma 14.5
  • Flutter:3.27.1、3.19.6(複数アプリで2種類のバージョンを使用)
  • Android Studio:Koala 2024.1.1 Patch 1

 

「OptionsViewOpenDirection.up」を設定すると表示されなくなる

調べたところ、下記スレッドに情報があり、「Autocomplete」の「optionsViewOpenDirection」というプロパティに、「OptionsViewOpenDirection.up」を設定すれば良いと分かりました。

 

 

しかし、これを設定すると、なぜか候補が表示されなくなりました。。

 

Chat-GPTに尋ねると、「optionsViewOpenDirection」プロパティを使用するのではなく、

 

「optionsViewBuilder」プロパティに設定している「ListView」を、
「FractionalTranslation(translation: const Offset(0, -1.0),・・・)」でラップして、表示位置を上にズラすと良い

 

という手法を提案されました。

 

やってみると、確かに上にズレて表示はできましたが、今後は、表示された候補リストの部分を触っても、スクロールや選択ができません

 

どうやらこの方法だと、表示自体はズレるのですが、タッチ判定の位置は上にズレてくれないようです。。

 

Alignの設定を組み合わせて解決

その後、試行錯誤しましたが、結論としては、

 

  • 「OptionsViewOpenDirection.up」を使用しつつ、
  • 候補表示の「ListView」を「Align」でラップし、
  • 「alignment」プロパティを「Alignment.bottomLeft」
    ※「Alignment.bottomCenter」や「Alignment.bottomRight」でも可

 

にすれば、上方に表示できました。

 

元々、「Align」でラップしてはいたのですが、「Alignment.topLeft」にしてしまっていたのが、不具合の要因だったようです。

 

その後、色々と試して分かったのですが、例えば、入力欄が画面最下部にあり、かつ、キーボードを非表示の状態にすると、「Alignment.topLeft」(「topRight」や「topCenter」も同じ)では、画面の上端に候補が表示され、「Alignment.centerLeft」にすると、画面の中央あたりに候補が表示されました。

 

この状態でキーボードが表示されたり、入力欄を画面中央に置いたりすれば、更に全体が上にズレるので、「Alignment.topLeft」や「Alignment.centerLeft」にすると、候補リストが画面から見えない位置に飛んでしまう、という事のようです。

 

ご参考に、今回作成した、DartPad上で挙動を試せる「Autocomplete」上方表示のコード例を掲載します。

 

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.end,
            children: [
              Autocomplete<String>(
                optionsViewOpenDirection: OptionsViewOpenDirection.up,
                optionsBuilder: (TextEditingValue textEditingValue) {
                  if (textEditingValue.text == '') {
                    return const Iterable<String>.empty();
                  }
                  return ['apple', 'banana', 'cherry']
                      .where((String option) {
                    return option.contains(textEditingValue.text.toLowerCase());
                  });
                },
                fieldViewBuilder: (BuildContext context,
                    TextEditingController textEditingController,
                    FocusNode focusNode,
                    VoidCallback onFieldSubmitted) {
                  return Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 20.0),
                    child: TextField(
                      controller: textEditingController,
                      focusNode: focusNode,
                      decoration: const InputDecoration(
                        border: OutlineInputBorder(),
                        labelText: 'フルーツを入力',
                      ),
                    ),
                  );
                },
                optionsViewBuilder: (BuildContext context,
                    AutocompleteOnSelected<String> onSelected,
                    Iterable<String> options) {
                  return Align(
                    alignment: Alignment.bottomLeft, // ←これが重要:リストを上方向に展開
                    child: Material(
                      elevation: 4.0,
                      child: SizedBox(
                        height: 200.0,
                        child: ListView.builder(
                          itemCount: options.length,
                          itemBuilder: (BuildContext context, int index) {
                            final String option = options.elementAt(index);
                            return ListTile(
                              title: Text(option),
                              onTap: () {
                                onSelected(option);
                              },
                            );
                          },
                        ),
                      ),
                    ),
                  );
                },
              ),
              const SizedBox(height: 50.0),
            ],
          ),
        ),
      ),
    );
  }
}

 

実際に実装する際には、画面内の「Autocomplete」の縦方向の位置に応じて、下方表示(OptionsViewOpenDirection.down)と、上方表示(OptionsViewOpenDirection.up)を切替えると、ユーザビリティが高いと思われます。

 

以上、どなたかのご参考になりましたら幸いです。

 

 

リリースしたアプリ(全てFlutterで開発)

 

個人アプリ開発で役立ったもの

おすすめの学習教材

超初心者向けでオススメな元Udemyの講座/

 

 \キャンペーン時を狙えば安価で網羅的な内容が学べる(日本語訳あり)/

 

\Gitの基礎について無料で学べる/

 

おすすめの学習書籍

実用的image_pickerに関してかなり助けられた/

 

Dartの基礎文法を素早くインプットできる/


Dart入門 - Dartの要点をつかむためのクイックツアー

コメント

タイトルとURLをコピーしました