Kodokon kodokon.com

フォーム:TextField、コントローラー、検証

TextEditingControllerで入力フィールドを操作し、FormとTextFormFieldのおかげですべての入力を一度に検証しましょう。

9 分 · 3 問

このレッスンを Kodokon で開く

コントローラーのない入力フィールドはブラックボックスです。TextEditingControllerはあなたに主導権を与えます。いつでもcontroller.textを読み取り、フィールドをあらかじめ埋め、送信後にクリアし、あるいはaddListenerで一打鍵ごとに耳を傾けられます。コントローラーはStateの中で作られ、決してbuildの中では作りません。さもないと再構築のたびに作り直されてしまいます。

DART
import 'package:flutter/material.dart';

class NicknameField extends StatefulWidget {
  const NicknameField({super.key});

  @override
  State<NicknameField> createState() =>
      _NicknameFieldState();
}

class _NicknameFieldState extends State<NicknameField> {
  final _controller = TextEditingController();

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      decoration: const InputDecoration(
        labelText: 'Nickname',
        border: OutlineInputBorder(),
      ),
      onSubmitted: (value) => debugPrint(value),
    );
  }
}
コントローラーはStateの中で生き、dispose()の中で死ぬ

画面に確認すべきフィールドが複数あるようになったら、FormGlobalKey<FormState>TextFormFieldの三点セットを採用しましょう。各フィールドはvalidatorの中で自身のルールを記述します。この関数はエラーメッセージを返すか、問題がなければnullを返します。キーに対してvalidate()を呼び出すと、すべてのバリデーターが一度に起動します。

DART
import 'package:flutter/material.dart';

class SignUpForm extends StatefulWidget {
  const SignUpForm({super.key});

  @override
  State<SignUpForm> createState() => _SignUpFormState();
}

class _SignUpFormState extends State<SignUpForm> {
  final _formKey = GlobalKey<FormState>();
  final _emailController = TextEditingController();

  @override
  void dispose() {
    _emailController.dispose();
    super.dispose();
  }

  void _submit() {
    if (_formKey.currentState!.validate()) {
      debugPrint('Email: ${_emailController.text}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TextFormField(
            controller: _emailController,
            keyboardType: TextInputType.emailAddress,
            decoration: const InputDecoration(
              labelText: 'Email',
            ),
            validator: (value) {
              if (value == null || value.isEmpty) {
                return 'Required field';
              }
              if (!value.contains('@')) {
                return 'Invalid address';
              }
              return null;
            },
          ),
          const SizedBox(height: 16),
          FilledButton(
            onPressed: _submit,
            child: const Text('Create account'),
          ),
        ],
      ),
    );
  }
}
validate()はフォーム内のすべてのバリデーターを起動する

ボタンがクリックされると、validate()は各validatorを実行します。返されたメッセージは該当するフィールドの下に赤で表示され、少なくとも一つのフィールドが無効ならメソッドはfalseを返します。したがって送信ロジックはたった一つのifの後ろに置かれ、無効なデータを送信することは不可能になります。

理解度チェック

このレッスンの要点をしっかり覚えているか確認しましょう。

  1. TextEditingControllerは何のためのものですか?
    • フィールドの枠線を描くため
    • フィールドのテキストを読み書きし、その変化に耳を傾けるため
    • メールアドレスを自動的に検証するため
    • キーボードを閉じるため
  2. 値が有効なとき、バリデーターは何を返すべきですか?
    • true
    • 空の文字列
    • null
    • 値そのもの
  3. なぜStateのdispose()メソッドの中で_controller.dispose()を呼ぶのですか?
    • 画面上のフィールドをクリアするため
    • リソースを解放し、メモリリークを避けるため
    • フォームを送信するため