MENU

Flutterでフォーム入力を実装する方法|初心者向けバリデーション

Flutterでフォーム入力を実装する方法|初心者向けバリデーション

Flutter フォーム入力は、TextFormFieldFormを組み合わせると実装しやすくなります。さらにvalidatorで入力内容を確認し、FormState.validate()trueのときだけ送信処理を実行するのが基本です。

この記事では、入力欄を1つ表示する最小構成から、未入力エラーの表示、ボタンを押したときの判定、初心者がつまずきやすい点までを順番に解説します。

初心者の疑問: TextFieldTextFormFieldは、どちらを使えばよいですか?

入力チェックを行うフォームなら、まずTextFormFieldを選ぶのがおすすめです。TextFormFieldTextFieldFormFieldとして扱えるため、Formのバリデーションと組み合わせられます。単純な検索欄など、個別のチェックが不要な場合はTextFieldでも問題ありません。

目次

Flutterのフォーム入力で使う部品

最初に、各部品の役割を整理しましょう。

部品役割初心者が覚えるポイント
Form複数の入力欄をまとめるフォーム全体を検証できる
GlobalKey<FormState>Formの状態を取得するvalidate()を呼ぶために使う
TextFormField文字入力欄を表示するvalidatorで入力チェックできる
validator入力が正しいか判定するエラー時は文字列、問題なければnullを返す
ElevatedButton送信ボタンを表示するvalidate()の結果を見て処理する

ポイント: フォーム入力は「表示」「検証」「送信」を分けて考えると、コードの役割が分かりやすくなります。

STEP1:フォーム用のStatefulWidgetを作る

GlobalKey<FormState>は、フォームの状態を参照するためのキーです。buildの中で毎回作り直すと状態管理が不安定になるため、Stateクラスのフィールドとして1回だけ作ります。

import 'package:flutter/material.dart';

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

  @override
  State<ContactForm> createState() => _ContactFormState();
}

class _ContactFormState extends State<ContactForm> {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: const Column(
        children: [],
      ),
    );
  }
}

Formは複数の入力欄をまとめるコンテナです。入力欄が1つでも、後から項目を増やす予定があるなら、この形で始めておくと拡張しやすくなります。

初心者の疑問: なぜStatelessWidgetではなくStatefulWidgetにするのですか?

フォームの状態を保持するGlobalKeyを、画面のライフサイクルに合わせて管理しやすくするためです。特に、buildのたびに新しいキーを作らないことが重要です。

STEP2:TextFormFieldに入力チェックを追加する

次に、Formの中へTextFormFieldを追加します。validatorは入力値を受け取り、問題があるときにエラーメッセージを返します。問題がなければnullを返してください。

TextFormField(
  decoration: const InputDecoration(
    labelText: '名前',
    hintText: '名前を入力してください',
    border: OutlineInputBorder(),
  ),
  validator: (value) {
    if (value == null || value.trim().isEmpty) {
      return '名前を入力してください';
    }
    return null;
  },
),

trim()を使うと、半角スペースだけを入力した場合も空欄として扱えます。メールアドレスや文字数などを検証したい場合も、同じvalidatorの中に条件を追加できます。

validator: (value) {
  final email = value?.trim() ?? '';
  if (email.isEmpty) {
    return 'メールアドレスを入力してください';
  }
  if (!email.contains('@')) {
    return 'メールアドレスの形式を確認してください';
  }
  return null;
},

注意: validatorで「問題ありません」と表示する必要はありません。エラーがないときはnullを返すのがFlutterのルールです。

STEP3:ボタンで検証してから送信する

入力欄と送信ボタンをFormの子要素に置き、ボタンのonPressedでフォームを検証します。

Form(
  key: _formKey,
  child: Column(
    children: [
      TextFormField(
        decoration: const InputDecoration(
          labelText: '名前',
          border: OutlineInputBorder(),
        ),
        validator: (value) {
          if (value == null || value.trim().isEmpty) {
            return '名前を入力してください';
          }
          return null;
        },
      ),
      const SizedBox(height: 16),
      ElevatedButton(
        onPressed: () {
          if (_formKey.currentState!.validate()) {
            ScaffoldMessenger.of(context).showSnackBar(
              const SnackBar(content: Text('入力を受け付けました')),
            );
          }
        },
        child: const Text('送信'),
      ),
    ],
  ),
)

validate()は、Formの中にある各フィールドのvalidatorを実行します。すべて問題なければtrue、1つでもエラーがあればfalseです。実際のアプリでは、trueのブロック内でAPI送信やデータ保存を行います。

初心者の疑問: 入力が空なのに送信処理が実行されるのはなぜですか?

TextFormFieldFormの子になっていない、ボタンで別のキーを使っている、またはvalidate()の結果を確認していない可能性があります。まず3つの部品が同じForm_formKeyにつながっているか確認してください。

完成した最小サンプル

画面として動かす場合は、次のコードをScaffoldbodyなどに配置できます。

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

  @override
  State<ContactForm> createState() => _ContactFormState();
}

class _ContactFormState extends State<ContactForm> {
  final _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Form(
        key: _formKey,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            TextFormField(
              decoration: const InputDecoration(
                labelText: '名前',
                border: OutlineInputBorder(),
              ),
              validator: (value) {
                if (value == null || value.trim().isEmpty) {
                  return '名前を入力してください';
                }
                return null;
              },
            ),
            const SizedBox(height: 16),
            ElevatedButton(
              onPressed: () {
                if (_formKey.currentState!.validate()) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    const SnackBar(content: Text('送信しました')),
                  );
                }
              },
              child: const Text('送信'),
            ),
          ],
        ),
      ),
    );
  }
}

チェックリスト: Formにキーを設定した、入力欄がTextFormFieldになっている、validatorがある、エラー時に文字列を返す、validate()trueのときだけ送信している、の5点を確認しましょう。

よくある失敗と改善方法

失敗1:キーをbuildの中で作る

// 避けたい例
final formKey = GlobalKey<FormState>();

これをbuild内に置くと、再ビルドのたびにキーが変わります。キーはStateクラスのフィールドに置きます。

失敗例: buildの中で毎回GlobalKeyを生成し、入力欄の状態や検証対象が期待どおりにならない。

改善例: final _formKey = GlobalKey<FormState>();Stateクラスに1つだけ定義する。

失敗2:見た目だけのTextFieldを使う

TextFieldでも文字は入力できますが、Formvalidate()で一括検証する用途にはTextFormFieldが向いています。入力チェックが必要かどうかで使い分けましょう。

失敗3:検証前にAPIへ送信する

送信ボタンを押したらすぐAPIを呼ぶのではなく、最初にvalidate()を実行します。さらに、送信中はボタンを無効にする、通信失敗時の表示を追加する、といった実装も本番では必要です。

補足: validatorは画面上の入力形式を確認する処理です。メールアドレスの存在確認やログイン認証の成否は、サーバー側の処理も必要です。

Flutterフォーム入力を実装したあとの次の作業

まずは名前の1項目で動作を確認し、その後にメールアドレスやパスワードを追加しましょう。複数項目にする場合も、各TextFormFieldvalidatorを設定し、最後に1回のvalidate()でまとめて判定できます。

次にやること: 名前の入力チェックを動かしたら、メールアドレスの形式チェックを追加し、空欄・スペースだけ・正しい入力の3パターンをテストしてください。

まとめ

Flutter フォーム入力の基本は、Formで入力欄をまとめ、TextFormFieldvalidatorでエラーを返し、GlobalKey<FormState>からvalidate()を呼ぶことです。

  1. StateクラスにGlobalKey<FormState>を作る
  2. Formにキーを設定する
  3. TextFormFieldvalidatorを追加する
  4. ボタンでvalidate()を呼び、trueのときだけ送信する

この順番で最小のフォームを作ってから、複数項目、文字数チェック、通信処理へ広げると、原因を切り分けながら実装できます。

参考情報

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

meibuのアバター meibu 中年ブロガー

【このブログを書いている人】
こんにちは、meibuです!
読者の「ちょっと気になる」「でも難しそう…」をやさしく解決したくてこのブログを始めました。

🔹プロフィールまとめ
◎ 本業は運送業の管理職
 → 現場からマネジメントまで幅広く経験

● 趣味はパソコン・ランニング・情報収集

●興味のあるテーマ:
 副業 / ChatGPT

● 発信スタイルは…
 → 「初心者にもわかりやすく」+「図解・たとえ話も使って解説」

● ブログの目的:
 日常に“ちょっと得する”選択肢を増やすこと
管理職の悩みを最近少し書いています

🔸こんな人に読んでほしい!
ChatGPTってなに?使い方がわからない方
管理職の職位に悩んでいる人
忙しいけど、自分の未来のために動きたい方

● 「読んでよかった!」と思える情報を、これからもコツコツ更新していきます!
当ブログはアフェリエイト広告を利用しています。

コメント

コメントする

CAPTCHA


目次