TextFormFieldで普通の入力フォームを作る – Flutter

[PR]

Flutterでいくつか入力パターンを作ったので忘れないようにメモ。
何回かに分けて書きますが、今回はベーシックな入力フォームを。

基本はこれ
https://medium.com/@anilcan/forms-in-flutter-6e1364eafdb5

ここからちょっと足したり、削ったり

削った部分

  • メールアドレスのバリデート
  • パスワード入力
    • TextFormFieldのobscureTextをtrueで伏せる
  • 文字数オーバーのバリデート
    • 代わりにmaxLengthとmaxLengthEnforcedで強制
      • 不親切な気もするけど、maxLength入れたら勝手に数値が出てくれるから充分
      • むしろ現在の文字数も分かるからよい
  • 余計なデザイン指定
    • 各々でどうぞ
    • ちゃんと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

 

フォーーム