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
State createState() => _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
