この記事について
Flutterで入力候補付きのテキストフィールドを表示するAutocomplete
ウィジェットの実装サンプルを紹介、解説しています。ここでは下記の3つタイプの実装サンプルを紹介しています。
- Flutter公式サンプル ← 一番簡単だが見た目×
- Medium(海外ブログサービス)で見つけたサンプル ← Flutter Webと相性×
- 僕がたどり着いた正解のサンプル ← 上記の欠点を克服!
答えだけ欲しい方は3つ目のサンプルコードだけコピペで使っていただければOKです◎
※ FlutterやDartの基礎的な部分についての解説は省いています
※ 可読性や簡単のため参考記事から一部コードを編集しています
Flutter AutoCompleteについて
Flutterの標準的なWidgetTextField
の変化系で、テキスト入力中に入力候補を表示させることができます。
その他多数の標準的なWidgetと共にflutter/material.dart
に含まれています。
公式ページ:Autocomplete class
material.dart
のインポート
import 'package:flutter/material.dart';
1. Flutter公式サンプル
Widget実装部コードサンプル
class MyAutocomplete extends StatelessWidget {
const MyAutocomplete({Key? key}) : super(key: key);
// 入力候補に出す値の配列
static const _kOptions = ['aardvark', 'bobcat', 'chameleon']; // 1.
@override
Widget build(BuildContext context) {
// AutoComplete Widget本体
return Autocomplete<String>( // 2.
// 入力候補リストの要素ビルダー
optionsBuilder: (TextEditingValue textEditingValue) { // 3.
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return _kOptions.where((String option) { // 4.
return option.contains(textEditingValue.text.toLowerCase());
});
},
// 入力候補選択時の処理
onSelected: (String selection) { // 5.
debugPrint('You just selected $selection');
},
);
}
}
解説
- 入力候補に表示する要素のデータ元になる配列
-
AutoComplete<String>
のように、リストに出す候補のクラスを宣言
(String
が返せるクラスであればなんでも可、今回はそのままString
) -
optionsBuilder
で、候補に出す値の配列を返します
(textEditingValue.text
でフィールドに入力中の値を取得してwhere
内で利用) - 2.で宣言したクラスの配列を
return
- 候補選択時の処理、次フィールドへフォーカスの移動なども実装可
参考ページ
2. Mediumで見つけたサンプル
特長
装飾を施したパターンの簡単なサンプル
デメリット
Webでこのまま実装した場合、キーボードの矢印による候補の選択ができなくなる
(厳密にはハイライトされなくなる、その他いろいろと難あり)
Widget実装部コードサンプル
class MyAutocomplete extends StatelessWidget {
const MyAutocomplete({Key? key}) : super(key: key);
static const _kOptions = ['aardvark', 'bobcat', 'chameleon'];
@override
Widget build(BuildContext context) {
return Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return _kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
onSelected: (String selection) {
debugPrint('You just selected $selection');
},
// ---------- ここから追加 ----------
optionsViewBuilder: (
BuildContext context,
AutocompleteOnSelected<String> onSelected,
Iterable<String> options,
) {
// 入力候補リストの表示枠のWidgetを定義
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4, // 1.
child: Container(
width: MediaQuery.of(context).size.width - 24, // 2.
color: Colors.cyan,
// のリストWidget本体
child: ListView.builder( // 3.
shrinkWrap: true, // 4.
itemCount: options.length,
itemBuilder: (context, index) {
final option = options.elementAt(index);
return GestureDetector(
child: ListTile(
title: Text(option, style: const TextStyle(color: Colors.white)),
),
onTap: () => onSelected(option), // 5.
);
},
),
),
),
);
},
// ---------- ここまで ----------
);
}
}
解説
- 影をつけるためのelevation
Material
ごと削除すれば影のないフラットなデザインに - テキストフィールドの幅に入力候補の幅もあわせる
デフォルトでは画面の右端まで突き抜けてしまう - 入力候補を動的に生成するリストビュー
- 候補が少ない時にリストが下の伸びっぱなしにならないよう
false
に - 候補選択時にフィールドに値が入力させる処理
onSelected
参考ページ
3. 僕がたどり着いた正解のサンプル
特長
入力候補部分のWidgetの装飾を変更しつつ、キーボード矢印による候補の選択にも対応
執筆時点では他サイトに類似のサンプルなどありませんでした!
(Autocompleteのソースコードを見てアレンジしました)
デメリット
特にありません、実際にアプリ制作する場合はこちらをお使いください
Widget実装部コードサンプル
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; // 1.
class MyAutocomplete extends StatelessWidget {
const MyAutocomplete({Key? key}) : super(key: key);
static const _kOptions = ['aardvark', 'bobcat', 'chameleon', 'dragonfly', 'eagle', 'fossa'];
@override
Widget build(BuildContext context) {
return Autocomplete<String>(
optionsBuilder: (TextEditingValue textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
}
return _kOptions.where((String option) {
return option.contains(textEditingValue.text.toLowerCase());
});
},
onSelected: (String selection) {
debugPrint('You just selected $selection');
},
optionsViewBuilder: (
BuildContext context,
AutocompleteOnSelected<String> onSelected,
Iterable<String> options,
) {
// 入力候補リストの表示枠のWidgetを定義
return Align(
alignment: Alignment.topLeft,
child: Material(
elevation: 4,
child: SizedBox( // 2.
width: MediaQuery.of(context).size.width - 24,
child: ConstrainedBox( // 3.
constraints: const BoxConstraints(maxHeight: 175),
child: ListView.builder(
shrinkWrap: true,
itemCount: options.length,
itemBuilder: (context, index) {
final option = options.elementAt(index);
return Builder(
builder: (BuildContext context) {
// 4.
final bool highlight = AutocompleteHighlightedOption.of(context) == index;
// 5.
if (highlight) {
SchedulerBinding.instance.addPostFrameCallback((Duration timeStamp) {
Scrollable.ensureVisible(context, alignment: 0.5);
});
}
return GestureDetector(
child: ListTile(
tileColor: highlight ? Colors.cyan.withAlpha(172) : Colors.cyan, // 6.
title: Text(option, style: const TextStyle(color: Colors.white)),
),
onTap: () => onSelected(option),
);
},
);
},
),
),
),
),
);
},
);
}
}
解説
- 5.で使用するコールバック関数のライブラリをインポート
- 色付きの
Container
から色なしのSizedBox
に変更 - 長いリストが画面外に出ないように
maxHeight
を設定 - 選択中の候補かどうかを
bool
で取得 - 選択中の候補がリスト表示枠の中心に来るように
callback
- 選択中の候補の色をハイライト表示
あとがき
参考になりましたらLGTMポチッとお願いします!
まだまだFlutter修行中ですが、ご質問などいただければ分かる範囲でお答え(もしかすると記事化)するので、ぜひコメントも残してください:)