ユーザー入力で使うwidget達
アプリやサービスでよく実装される機能としてユーザーの文字入力を受け取る機能があるかと思います。
Flutterではこのユーザー入力に対して、TextField
もしくは TextFormField
というwidgetが用いられます。
しかしこれらは一見同じような機能を持ったwidgetとなっており、どちらを使用するか迷ってしまうことも多いので、簡単に説明していきたいと思います。
TextField widget
TextField
はシンプルにユーザーの文字入力を受け取るwidgetです。
TextField widget
TextField(
controller: TextEditingController(text: 'test'),
onSubmitted: (value) {
// do something
},
),

シンプルとはいえ様々な機能を備えており、Enterボタンで実行するコールバック(onSubmitted
)やパスワード入力などで使う入力値をマスクする機能(obscureText
)などがデフォルトで備わっています。
上記のコードサンプルの様に TextEditingController
という入力値を管理する状態クラスと組み合わせて使うのが一般的です。
TextFormField widget
TextFormField
も同様にユーザーの文字入力を受け取るwidgetです。
いくつか異なるフィールドを持っていますが、最大の違いは validator
という入力値の検証を行うフィールドを持っていることです。
TextFormField widget
TextFormField(
controller: TextEditingController(text: 'これはTextFormField'),
onFieldSubmitted: (value){
// do something
},
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (value){ // 入力値を受け取る
if(value == null || value.isEmpty){
return '値を入力してください'; // エラーメッセージとして表示される
}
return null;
},
),

validator
は入力値を受け取るコールバック関数を受け取るのですが、文字列をreturn
するとその文字列がエラーメッセージとしてwidget下に表示されます。この機能を使うことでユーザーに指定の入力値を入れるように促すことが可能です。
この機能に加えて、もう1つTextFormField
には大きな特徴があります。それが Form
widgetと組み合わせることで機能を拡張することが出来る 点です。
Form widget
突然出てきた Form
widgetですが、これは複数の入力欄を管理する為のwidgetです。ネットの会員登録などでよく見る申込フォームをイメージしていただくと分かりやすいかと思います。

複数の入力欄が並び、中には必須項目があったり、使える文字の制限などがあったりするかと思います。その内、正しく入力されていない項目があると申請ボタンが押せないといった機能もあったりするかと思います。
こういった複雑なフォームとしての機能を実現する為のwidgetが Form
widget です。ただ、Form
単体ではフォーム機能を実現する事はできません。
Form
はあくまでも複数の入力欄などを管理するwidgetとなるので、実際の入力欄を設置する必要があります。この際に用いられるのが TextFormField
です。
ではなぜTextFormField
単体ではなく、Form
widgetを使う必要があるのでしょうか?
Form x TextFormField => 一斉バリデーション!
Form
x TextFormField
の組み合わせを使う一番のモチベーションは、複数の入力欄を一括で管理するForm
を使うことで、複数のTextFormField
を一斉に検証することが可能になる点です。
Form
widgetは配下に配置されたTextFormField
全てと連携する機能が備わっており、それにより一斉検証を行うメソッドを実行することができます。
Formで複数のTextFormFieldを一斉検証する
以下のように2つの記入欄をボタンクリックで一斉検証するコードを例に Form + TextFormFieldを使った一斉検証の実装方法を見ていきましょう。
1. Formの配下に複数のTextFormFieldを配置
まず以下のようにForm
widgetの配下に複数のTextFormField
を配置します。ポイントは TextFormField
はForm
直下でなくても構わない 点です。
child: Form( // <----- この配下に
key: formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(32.0),
child: TextFormField( // <------ TextFormFIeldを配置
...
),
),
Padding(
padding: const EdgeInsets.all(32.0),
child: TextFormField( // <------- TextFormFIeldを配置
...
),
),
],
),
),
2. TextFormFieldに各々のvalidatorを定義
次にTextFormFieldには各々のvalidatorを定義しておきます。サンプルではアルファベットのみを許容する入力欄とひらがなのみを許容する入力欄を定義しています。
children: <Widget>[
Padding(
padding: const EdgeInsets.all(32.0),
child: TextFormField(
...
validator: (value) { // <------ validatorを定義
if (value == null || value.isEmpty) {
return '値を入力してください';
}
// アルファベットのみを許容する
if (!RegExp(r'^[a-zA-Z]+$').hasMatch(value)) {
return 'アルファベットのみを入力してください';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.all(32.0),
child: TextFormField(
...
validator: (value) { // <------ validatorを定義
if (value == null || value.isEmpty) {
return '値を入力してください';
}
// ひらがなのみを許容する
if (!RegExp(r'^[ぁ-んー]+$').hasMatch(value)) {
return 'ひらがなのみを入力してください';
}
return null;
},
),
),
],
3. Formを特定するGlobalKeyを定義
次にGlobalKey<FormState>
というクラスの変数を定義し、これをForm
widgetのkey
というパラメータに渡します。
class _MyHomePageState extends State<MyHomePage> {
final formKey = GlobalKey<FormState>(); // GlobalKeyを発行
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Form(
key: formKey, // <----- keyパラメータに渡す
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// TextFormField
// TextFormField
],
),
),
),
// FloatingActionButton
);
}
}
このGlobalKey
(もしくはkey
)というのはwidgetツリー内で任意のwidgetを特定するのに用いられます。
4. GlobalKeyを使って、validateメソッドを実行する
最後にForm
に渡したkey
を通して、バリデーションを実行するvalidate()
メソッドというのを実行します。
key
は先ほどForm
に渡してあるので、それによってどのForm
のvalidate()
メソッドを呼び出せば良いのかが分かります。
この validate()
メソッドには「配下の全てのTextFormField
のvalidator
フィールドに定義された処理を実行する」という処理が定義されています。
floatingActionButton: FloatingActionButton(
onPressed: () {
formKey.currentState?.validate(); // <-- 配下の全TextFormFieldのバリデーションを実行
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
🚀 まとめ
以上、Form
とTextFormField
がどういったwidgetなのかを解説したところで、これらを早速使ってログインページ、サインアップページを作ってみましょう!