Flutterでいくつか入力パターンを作ったので忘れないようにメモ。
何回かに分けて書きますが、今回はベーシックな入力フォームを。
基本はこれ
https://medium.com/@anilcan/forms-in-flutter-6e1364eafdb5
ここからちょっと足したり、削ったり
削った部分
- メールアドレスのバリデート
- validateプラグインを使ってるっぽいです
- パスワード入力
- TextFormFieldのobscureTextをtrueで伏せる
- 文字数オーバーのバリデート
- 代わりにmaxLengthとmaxLengthEnforcedで強制
- 不親切な気もするけど、maxLength入れたら勝手に数値が出てくれるから充分
- むしろ現在の文字数も分かるからよい
- 代わりにmaxLengthとmaxLengthEnforcedで強制
- 余計なデザイン指定
- 各々でどうぞ
- ちゃんとThemeでカスタマイズしておけば、あんまり指定はいらないはず
足した部分は後で補足。
ソース
定数とか除外したのでコピペでいけるはず。
import 'package:flutter/material.dart'; class FormSubmitPage extends StatefulWidget { @override StatecreateState() => _FormSubmitPageState(); } class _ProfileData { String name = ''; String description = ''; } // 必須チェック FormFieldValidator _requiredValidator(BuildContext context) => (val) => val.isEmpty ? "必須" : null; class _FormSubmitPageState extends State { final GlobalKey _formKey = GlobalKey (); _ProfileData _data = _ProfileData(); FocusNode _nameFocusNode; FocusNode _descriptionFocusNode; void _submit() { // バリデートして問題なければ実行 if (this._formKey.currentState.validate()) { // TextFormField の onSavedを実行 _formKey.currentState.save(); // 入力内容 debugPrint('Name: ${_data.name}'); debugPrint('Description: ${_data.description}'); // キーボードを隠す(それぞれのonSavedに書いたほうがいいかも) _nameFocusNode.unfocus(); _descriptionFocusNode.unfocus(); // TODO 送信処理 } } @override void initState() { super.initState(); _nameFocusNode = FocusNode(); _descriptionFocusNode = FocusNode(); } @override void dispose() { _nameFocusNode.dispose(); _descriptionFocusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('📮')), body: Padding( padding: EdgeInsets.all(16.0), child: Form( key: this._formKey, child: ListView( children: [ TextFormField( decoration: InputDecoration(labelText: '名前', border: OutlineInputBorder()), validator: _requiredValidator(context), maxLength: 12, maxLengthEnforced: true, focusNode: _nameFocusNode, onSaved: (String value) => this._data.name = value, // focus当てとく autofocus: true, // focus移動 textInputAction: TextInputAction.next, onFieldSubmitted: (_) => FocusScope.of(context).requestFocus(_descriptionFocusNode), ), SizedBox(height: 16.0), TextFormField( decoration: InputDecoration(labelText: '自己紹介', border: OutlineInputBorder()), validator: _requiredValidator(context), maxLength: 60, maxLengthEnforced: true, focusNode: _descriptionFocusNode, onSaved: (String value) => this._data.description = value, // 複数行対応 keyboardType: TextInputType.multiline, maxLines: null, ), SizedBox(height: 32.0), RaisedButton(child: Text('Submit'), onPressed: this._submit) ], ), )), ); } }
画面を呼び出す処理(UI省略)
Navigator.push(context, MaterialPageRoute(builder: (context) => FormSubmitPage()));
足した部分
複数行対応
TextFormFieldに以下の2つを追加
・keyboardType: TextInputType.multiline (改行キーボード)
・maxLines: null (テキスト量に合わせた高さに調整)
補足として、maxLinesがnullだと、どんどん高さが増してしまう。
でも、maxLinesに1以外の数値を入れると、最初からその行数になってしまう。
どうやったらテキスト量に高さを合わせつつ、最大行数を制限できるかは分からないっす。うっす。
フォーカス管理
今回、FocusNodeを使ってやってることはフォーカスの管理のみで、イベントリスナーは別の記事で書くと思います。
やってること
・initStateで初期化
・TextFormFieldのfocusNodeにセット
・submit時にunfocus()でキーボードをしまう
・FocusScope.of(context).requestFocus([FocusNode])で任意のTextFormFieldにフォーカスを当てる
画面起動時にautofocus:trueで最初のTextFormFieldを当ててキーボードを表示。
キーボードの決定ボタンの処理(onFieldSubmitted)で次のTextFormFieldにrequestFocusでフォーカスを移動するといった感じ。
あと、textInputAction: TextInputAction.next でキーボードの決定ボタンの見た目も変えてる。
参考: https://flutter.io/docs/cookbook/forms/focus