テキストフィールドにユーザが情報を入力することが必要な場面がapp(アプリケーション・サービス)にはあります。例えば、メールアドレスとパスワードのセットを用いて、ユーザーにログインしてもらう必要がある場合です。
appを安全で利用しやすいものにするために、ユーザーから提供されたデータが有効なデータかを確認する必要があります。もしユーザーが正しいデータをフォームに入力した場合、処理を進めます。不正なデータでサブミットしようとした場合、エラーメッセージを表示して、何が問題かをユーザーに示す必要があります。
このサンプルでは、一つのテキストフィールドを持つフォームにバリデーションを追加する方法を以下のステップで学んでいきます。
- GlobalKeyを用いてFormを生成する。
- TextFormFieldをバリデーションロジックと共に追加する。
- バリデーションとサブミットを行うためのボタンを設置する。
Contents
1.GlobalKeyを用いてFormを生成する。
まずFormを生成します。Formウィジェットは複数のフォームフィールドをグループ化しバリデートするためのコンテナとしての役割を持ちます。
フォームを生成する時、GlobalKeyを用意します。このGlobalKeyによりそれぞれのフォームを一意(ユニーク)に識別します。そしてこの後のステップでバリデートできるようになります。
// カスタムフォームウィジェットを定義します。 class MyCustomForm extends StatefulWidget { @override MyCustomFormState createState() { return MyCustomFormState(); } } // MyCustomFormウィジェットに対応するStateクラスを定義します。 // このクラスがフォームに関するデータを保持します。 class MyCustomFormState extends State<MyCustomForm> { // フォームウィジェットを一意(ユニーク)に識別するし、 // フォームのバリデーションを可能にするためにglobal keyを生成します。 // // Note:`GlobalKey<FormState>`であり、 // ↓GlobalKey<MyCustomFormState>.ではないので注意してください。 final _formKey = GlobalKey<FormState>(); @override Widget build(BuildContext context) { // Build a Formウィジェットをビルドして、keyパラメータに、先ほど // 生成した_formKeyを指定します。 return Form( key: _formKey, child: Column( children: <Widget>[ // ここにTextFormFields と ElevatedButton を配置します。 ] ) ); } }
Tip:GlobalKeyを使うFormにアクセスするのにおすすめの方法です。
しかし、あなたのアプリのウィジェットツリーがもっと複雑な場合、Form.of()メソッドによりネストしたウィジェット群からフォームにアクセスする方法もあります。
2.バリデーションロジックと共にTextFormFieldを追加する。
Formを設置しても、それだけではユーザーが文字を入力するユーザーインターフェースが無い状態です。それはTextFormFieldウィジェットの仕事です。TextFormFieldウィジェットは、マテリアルデザインのテキストフィールドを描画し、バリデーションエラーが発生した時にその旨を表示することができます。
TextFormFieldウィジェットのvalidatorパラメータにコールバック(無名関数)を設定することでバリデーションロジックを指定します。ユーザーの入力が不正と判断された場合、validatorコールバックはエラーメッセージを含む文字列を返します。エラーが無い場合(バリデーションの結果有効な入力と判断された場合)、validatorコールバックはnullを返すようにします。
下記のサンプルは、何らかの入力が必要なTextFormFieldのためのvalidatorコールバックの例です。もし入力が無い(入力が空(から))の場合、親切なエラーメッセージを返します。
TextFormField(
// The validator receives the text that the user has entered.
//vqlidatorコールバックは引数として入力された文字列を受け取るので、
//それを基にバリデーションコードを記述する。
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
);
3.フォームをバリデートし、サブミットするボタンを配置する。
テキストフィールドとフォームを設置しました。次にユーザーがデータをサブミット(送信・提出)するために押すボタンを設置しましょう。
ユーザーがサブミットボタンを押してフォームのサブミットを試みた時、フォームが不正でないかチェック(バリデート)します。バリデート成功(有効なデータ)の場合、成功した旨のメッセージをユーザーに対して表示します。
不正なデータの場合(テキストフィールドが空だった場合)、エラーメッセージを表示します。
ElevatedButton( onPressed: () { // ↓バリデート成功の場合trueを返します。バリデート失敗の場合falseを返します。 if (_formKey.currentState.validate()) { //ここではバリデート成功の場合スナックバーを表示します。 //実際は、バリデート成功後サーバーに連絡したりデータベースにデータを //書き込んだりすることが多いです。 Scaffold .of(context) .showSnackBar(SnackBar(content: Text('Processing Data'))); } }, child: Text('Submit'), );
どのように動きますか?
フォームをバリデートするために、step1で作った_formKeyを使います。
_formKey.currentState()メソッドを使ってFormStateにアクセスすることができます。FormStateは、Formをビルド(生成)した時にFlutterによって自動的に生成されます。
_formKey.currentState()メソッド、というなら上記サンプルのif文の条件も
if( _formKey.currentState().validate() )
となるのではないか?と思うがとりあえず原文通り訳す。
FormStateクラスにはvalidate()メソッドがあります。validate()メソッドが呼び出されると、フォーム内のそれぞれのテキストフィールド(TextFormFieldウィジェット)に設定したvalidator()メソッドを実行します。
フォーム内の全てのテキストフィールドのバリデートが成功した場合、validate()メソッドはtrueを返します。
フォーム内のどこかのテキストフィールドでエラーが発生した(バリデートの結果が失敗だった)場合、validate()メソッドはエラーメッセージを表示するためフォームをリビルドし、falseを返します。
import 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { final appTitle = 'Form Validation Demo'; return MaterialApp( title: appTitle, home: Scaffold( appBar: AppBar( title: Text(appTitle), ), body: MyCustomForm(), ), ); } } // Create a Form widget. class MyCustomForm extends StatefulWidget { @override MyCustomFormState createState() { return MyCustomFormState(); } } // Create a corresponding State class. // This class holds data related to the form. class MyCustomFormState extends State<MyCustomForm> { // Create a global key that uniquely identifies the Form widget // and allows validation of the form. // // Note: This is a GlobalKey<FormState>, // not a GlobalKey<MyCustomFormState>. final _formKey = GlobalKey<FormState>(); @override Widget build(BuildContext context) { // Build a Form widget using the _formKey created above. return Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ TextFormField( validator: (value) { if (value.isEmpty) { return 'Please enter some text'; } return null; }, ), Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: ElevatedButton( onPressed: () { // Validate returns true if the form is valid, or false // otherwise. if (_formKey.currentState.validate()) { // If the form is valid, display a Snackbar. Scaffold.of(context) .showSnackBar(SnackBar(content: Text('Processing Data'))); } }, child: Text('Submit'), ), ), ], ), ); } }
参考