広告 Flutter Dart Unity アプリ開発

TextField、TextFormField、Formの概要

この記事は、単なるコピペです。後で検証して整理します。

ユーザー入力で使うwidget達

アプリやサービスでよく実装される機能としてユーザーの文字入力を受け取る機能があるかと思います。

Flutterではこのユーザー入力に対して、TextField もしくは TextFormField というwidgetが用いられます。

しかしこれらは一見同じような機能を持ったwidgetとなっており、どちらを使用するか迷ってしまうことも多いので、簡単に説明していきたいと思います。

TextField widget

TextFieldはシンプルにユーザーの文字入力を受け取るwidgetです。

TextField widget

    TextField(
        controller: TextEditingController(text: 'test'),
        onSubmitted: (value) {
            // do something
        },
    ),

シンプルとはいえ様々な機能を備えており、Enterボタンで実行するコールバック(onSubmitted)やパスワード入力などで使う入力値をマスクする機能(obscureText)などがデフォルトで備わっています。

上記のコードサンプルの様に TextEditingController という入力値を管理する状態クラスと組み合わせて使うのが一般的です。

TextFormField widget

TextFormFieldも同様にユーザーの文字入力を受け取るwidgetです。

いくつか異なるフィールドを持っていますが、最大の違いは validator という入力値の検証を行うフィールドを持っていることです。

TextFormField widget

TextFormField(
    controller: TextEditingController(text: 'これはTextFormField'),
    onFieldSubmitted: (value){
    // do something
    },
    autovalidateMode: AutovalidateMode.onUserInteraction,
    validator: (value){ // 入力値を受け取る
        if(value == null || value.isEmpty){
            return '値を入力してください'; // エラーメッセージとして表示される
        }
        return null;
    },
),

validatorは入力値を受け取るコールバック関数を受け取るのですが、文字列をreturnするとその文字列がエラーメッセージとしてwidget下に表示されます。この機能を使うことでユーザーに指定の入力値を入れるように促すことが可能です。

この機能に加えて、もう1つTextFormFieldには大きな特徴があります。それが Formwidgetと組み合わせることで機能を拡張することが出来る 点です。

Form widget

突然出てきた Form widgetですが、これは複数の入力欄を管理する為のwidgetです。ネットの会員登録などでよく見る申込フォームをイメージしていただくと分かりやすいかと思います。

複数の入力欄が並び、中には必須項目があったり、使える文字の制限などがあったりするかと思います。その内、正しく入力されていない項目があると申請ボタンが押せないといった機能もあったりするかと思います。

こういった複雑なフォームとしての機能を実現する為のwidgetが Form widget です。ただ、Form単体ではフォーム機能を実現する事はできません。

Formはあくまでも複数の入力欄などを管理するwidgetとなるので、実際の入力欄を設置する必要があります。この際に用いられるのが TextFormField です。

ではなぜTextFormField単体ではなく、Formwidgetを使う必要があるのでしょうか?

Form x TextFormField => 一斉バリデーション!

Form x TextFormField の組み合わせを使う一番のモチベーションは、複数の入力欄を一括で管理するFormを使うことで、複数のTextFormFieldを一斉に検証することが可能になる点です。

Formwidgetは配下に配置されたTextFormField全てと連携する機能が備わっており、それにより一斉検証を行うメソッドを実行することができます。

Formで複数のTextFormFieldを一斉検証する

以下のように2つの記入欄をボタンクリックで一斉検証するコードを例に Form + TextFormFieldを使った一斉検証の実装方法を見ていきましょう。

1. Formの配下に複数のTextFormFieldを配置

まず以下のようにFormwidgetの配下に複数のTextFormFieldを配置します。ポイントは TextFormFieldForm直下でなくても構わない 点です。

child: Form( // <----- この配下に
    key: formKey,
    child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
            Padding(
                padding: const EdgeInsets.all(32.0),
                child: TextFormField( // <------ TextFormFIeldを配置
                    ...
                ),
            ),
            Padding(
                padding: const EdgeInsets.all(32.0),
                child: TextFormField( // <------- TextFormFIeldを配置
                    ...
                ),
            ),
        ],
    ),
),

2. TextFormFieldに各々のvalidatorを定義

次にTextFormFieldには各々のvalidatorを定義しておきます。サンプルではアルファベットのみを許容する入力欄とひらがなのみを許容する入力欄を定義しています。

children: <Widget>[
    Padding(
        padding: const EdgeInsets.all(32.0),
        child: TextFormField(
            ...
            validator: (value) { // <------ validatorを定義
                if (value == null || value.isEmpty) {
                    return '値を入力してください';
                }
                // アルファベットのみを許容する
                if (!RegExp(r'^[a-zA-Z]+$').hasMatch(value)) {
                    return 'アルファベットのみを入力してください';
                }
                return null;
            },
        ),
    ),
    Padding(
        padding: const EdgeInsets.all(32.0),
        child: TextFormField(
            ...
            validator: (value) { // <------ validatorを定義
                if (value == null || value.isEmpty) {
                    return '値を入力してください';
                }
                // ひらがなのみを許容する
                if (!RegExp(r'^[ぁ-んー]+$').hasMatch(value)) {
                    return 'ひらがなのみを入力してください';
                }
                return null;
            },
        ),
    ),
],

3. Formを特定するGlobalKeyを定義

次にGlobalKey<FormState>というクラスの変数を定義し、これをFormwidgetのkeyというパラメータに渡します。

class _MyHomePageState extends State<MyHomePage> {
  final formKey = GlobalKey<FormState>(); // GlobalKeyを発行

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Form(
          key: formKey, // <----- keyパラメータに渡す
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
                // TextFormField
                // TextFormField
            ],
          ),
        ),
      ),
      // FloatingActionButton
    );
  }
}

このGlobalKey(もしくはkey)というのはwidgetツリー内で任意のwidgetを特定するのに用いられます。

4. GlobalKeyを使って、validateメソッドを実行する

最後にFormに渡したkeyを通して、バリデーションを実行するvalidate()メソッドというのを実行します。

keyは先ほどFormに渡してあるので、それによってどのFormvalidate()メソッドを呼び出せば良いのかが分かります。

この validate() メソッドには「配下の全てのTextFormFieldvalidatorフィールドに定義された処理を実行する」という処理が定義されています。

floatingActionButton: FloatingActionButton(
    onPressed: () {
        formKey.currentState?.validate(); // <-- 配下の全TextFormFieldのバリデーションを実行
    },
    tooltip: 'Increment',
    child: const Icon(Icons.add),
),

🚀 まとめ

以上、FormTextFormFieldがどういったwidgetなのかを解説したところで、これらを早速使ってログインページ、サインアップページを作ってみましょう!

-Flutter Dart Unity アプリ開発