TextFormFieldの入力値の設定と取得
前回までは主にテキストフィールドの表示方法についての説明でした。実際の利用ではサーバから取得したデータを入力フォームに設定したり、また、入力されたデータをサーバ上に保存したりします。その際は、必ず入力チェックなども必要です。今回は、このような場合に必要なTextFormFieldのデータの取り扱い方法を説明します。
初期値の設定
まず最初に紹介するのが、コンポーネントを作成するときに初期値を設定する方法がリスト1です。一般的に初期値がプログラムで決められている場合に利用できる方法です。外部からデータを取得する場合であっても、画面作成時にはすでにデータが用意されている場合であれば利用可能です。
TextFormField( initialValue: "INIT VALUE", ),
入力された値の取得方法
続いて、入力された値を取得する方法を2つ紹介します。最初の方法が入力された値が変わったときに値を取得する方法です。値が変わったときのイベントを利用する方法で、その実装例がリスト2です。
// (省略) // (1) 格納する文字列変数 String _loginId = ""; @override Widget build(BuildContext context) { return Center( child: Padding( padding: EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextFormField( // (2) 値が変わったときの処理 onChanged: (value) { setState(() { _loginId = value; }); }, ), // (省略) Text(_loginId), ] ) ) ) } // (省略)
(1)は入力された文字列を格納する変数を用意しています。続いて、値が変更されたタイミングでonChangedで指定された関数が実行されるので、(2)のように値を設定する処理を記述します。このonChangedは値が変わる度に実行されます。JavaScriptなどで開発経験があれば、直感的に理解できる方法だと思います。
続いて、入力が変化される度に入力値を知る必要がない場合があります。例えば、会員の登録画面などを想像してもらえばわかりやすいはずです。一般的に登録ボタンなどがあり、そのボタンを押したタイミングに設定されている値を取得したいというようなケースです。
そのような場合に利用できるのがTextEditingControllerを利用する方法です。その実装例がリスト3です。
// (1) コントローラ final TextEditingController _textController = TextEditingController(); String _loginId = ""; @override void dispose() { // (2) 必ず必要な処理 _textController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Center( child: Padding( padding: EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // TextFormField( // (3) 対象のコンポーネントに設定する controller: _textController, ), ElevatedButton( onPressed: () { setState(() { // (4) 値を取得する _loginId = _textController.text; }); }, child: Text("Submit")), // 省略 ElevatedButton( onPressed: () { // (5) 値を設定する _textController.text = "ABCD1234"; }, child: Text("Set Value")), ], ))); } @override void initState() { super.initState(); // (6) 値が変わったときの処理を登録 _textController.addListener(() { print("changed value [${_textController.text}]"); }); }
まず、(1)のようにTextEditingControllerを作成します。忘れていけないのが、(2)のように画面が削除されるタイミングでTextEditingControllerの削除処理を実行する必要があります。TextFormFieldに(3)のように設定すると値をコントロールすることができます。値を取得する場合には、(4)のようにtextプロパティを読めばよく、値を設定する場合には(5)のようにします。
コントローラを使っている場合にonChangedと同様に値が変わったときに処理を実行したい場合には(6)のように指定することができます。このようにTextEditingControllerを利用すればより高度のコントロールすることが可能です。こちらは、どちらかと言えばネイティブアプリの文化に近い形式のコードスタイルになります。
外部から値を設定する必要がある場合には、こちらの方式を利用することになります。また、取得だけであればどちらでも可能なので、好きなほうを選んでよいでしょう。
入力チェックとエラーメッセージの設定
前回、TextFormFieldのerrorTextに値を設定することでエラーチェックとエラーメッセージを表示することができるということを紹介しました。しかし、一般的にはアプリ内で入力チェックを行い、そのチェック結果に応じたエラーメッセージを設定するという利用をよくします。そのような方法がリスト4です。
// (1) Form用のキーを作成する final _formState = GlobalKey<FormState>(); @override Widget build(BuildContext context) { // (2) Formを使う return Form( // (3) 設定する key: _formState, child: Padding( padding: EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextFormField( // (4) 値をチェックする validator: (value){ if(value == null || value.isEmpty){ return "入力してください"; } return null; } ), ElevatedButton( onPressed: () { // (5) 値をチェックする _formState.currentState!.validate(); }, child: Text("値をチェックする")), ], ))); }
GlobalKeyを使うとコンポーネントの実体(State)を参照することができます。FlutterではUIを宣言型で定義していくために、宣言したコンポーネントのインスタンスに相当するような実体にアクセスすることが難しくなります。
特に、コンポーネントのツリー構造にとらわれず参照したいことが多くなります。そのため、(1)のように作成したキーを(2)のようにFormコンポーネントの(3)のように設定します。
Formコンポーネントは、FormFieldを扱うためのコンテナコンポーネントです。これまで紹介しませんでしたが、テキスト入力コンポーネントにはTextFieldというものがあります。
TextFieldとTextFormFieldの違いはほとんどなく、Formコンポーネントが管理できるようにしたTextFieldがTextFormFieldです。従って、Formと共に利用する場合には、TextFormFieldを必ず使用する必要があります。そして、値をチェックするルールを(4)のように記述することができ、エラーメッセージを返せば自動的に設定されます。値のチェックをするタイミングは(5)のようにボタンを押したタイミングなどのように任意に指定できます。
ラジオボタン(Radio)
続いて、ラジオ(Radio)について紹介します。ラジオボタンは複数の選択肢の中から1つだけ選択する場合に利用する形式で、図1のような表示になります。また、その実行コード例がリスト5です。
class _RadioWidget extends State<RadioWidget> { // (1) 選択済みの値を保存する変数 int _selectedValue = 1; @override Widget build(BuildContext context) { return Container( color: Colors.white, alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ListTile( title : Text('Value 1'), leading: createRadio(1), ), // : (省略) ]) ); } Radio createRadio(int dataValue){ return Radio( // (2) 選択グループ groupValue: _selectedValue, // (3) 値が変わったとき onChanged: (value) { setState(() { _selectedValue = value; }); }, // (4) 選択されたときの値 value: dataValue, ); } // : (省略) // (5) 利用できないボタン Radio createDisableRadio(){ return Radio( onChanged: null, // : (省略) ); } }
(1)は選択済みの値を保存する変数です。この変数は(2)のようにgroupValueプロパティに設定することで、複数のラジオボタンをグループ化して扱うことができます。チェックされたときには(3)のようにonChangedメソッドが実行されるので、そこで(1)での変数に値を設定します。そして、(4)が選択されたときの値を指定します。TextFormFieldと同様にonChangedにnullを設定すると、選択できないコントロールとして扱えます。