Flutter

入力フォームと検証

TextFormFieldで文字と数値を受け取り、入力の誤りを画面で説明します。

対象:Dart 3を同梱したFlutter 3系のstable版を対象に、まずChromeで動かすWebアプリを作ります。

01

入力・検証・表示の流れ

Flutter入門のStatefulWidgetとsetStateを使います。study_appのlib/main.dartを完成例に置き換えて flutter run -d chrome で実行します。学習内容と1〜1440分の時間を受け取り、両方正しければ画面に結果を表示します。保存先はメモリーで、再起動すると消えます。

02

フォームの完成例

Formは入力欄をまとめ、GlobalKeyでその検証処理へアクセスします。TextEditingControllerで現在の入力を読みます。validatorは不正なら説明文、正しければnullを返します。

lib/main.dart(全体を置き換え)DART
import 'package:flutter/material.dart';

void main() => runApp(const StudyApp());
class StudyApp extends StatelessWidget {
  const StudyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: MemoFormPage());
  }
}
class MemoFormPage extends StatefulWidget {
  const MemoFormPage({super.key});
  @override
  State<MemoFormPage> createState() => _MemoFormPageState();
}
class _MemoFormPageState extends State<MemoFormPage> {
  final _formKey = GlobalKey<FormState>();
  final _titleController = TextEditingController();
  final _minutesController = TextEditingController();
  String _message = '未登録';

  @override
  void dispose() {
    _titleController.dispose();
    _minutesController.dispose();
    super.dispose();
  }

  void _submit() {
    if (!(_formKey.currentState?.validate() ?? false)) return;
    final title = _titleController.text.trim();
    final minutes = int.tryParse(_minutesController.text.trim());
    if (minutes == null) return;
    setState(() { _message = '${title}を${minutes}分学習しました'; });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('学習メモの入力')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Form(
          key: _formKey,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              TextFormField(
                controller: _titleController,
                decoration: const InputDecoration(labelText: '学習内容'),
                validator: (value) {
                  if (value == null || value.trim().isEmpty) {
                    return '学習内容を入力してください';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              TextFormField(
                controller: _minutesController,
                decoration: const InputDecoration(labelText: '学習時間(分)'),
                keyboardType: TextInputType.number,
                validator: (value) {
                  final minutes = int.tryParse(value?.trim() ?? '');
                  if (minutes == null || minutes < 1 || minutes > 1440) {
                    return '1〜1440の整数を入力してください';
                  }
                  return null;
                },
              ),
              const SizedBox(height: 16),
              ElevatedButton(onPressed: _submit, child: const Text('登録')),
              const SizedBox(height: 16),
              Text(_message),
            ],
          ),
        ),
      ),
    );
  }
}
画面で確認する結果OUTPUT
空欄で「登録」 → 各入力欄にエラーが出る
学習内容「Dart」、時間「20」で登録 → Dartを20分学習しました
時間「abc」「0」「1441」 → 1〜1440の整数を入力してください
03

キーボードと検証は別

TextInputType.numberは数値入力向けキーボードの指定です。貼り付けなどで数値以外も入るため、int.tryParseでの検証は必要です。trimで空白を除いてから検証し、空白だけの学習内容も拒否します。

登録ボタンを押すとvalidateが全入力欄のvalidatorを呼びます。1つでも不正なら早期returnし、前の登録結果を書き換えません。正常に登録した後、不正な時間で再登録しても前の結果が残ることを確かめてください。

確認する結果OUTPUT
Dart・20で登録 → Dartを20分学習しました
時間を0にして登録 → エラーが出て、登録結果は前のまま
04

Controllerの後片付け

ControllerとGlobalKeyはbuildの外で1回だけ作ります。build内で作り直すと入力や検証の状態が失われる原因になります。Widgetが不要になるとdisposeが呼ばれるので、そこで2つのControllerもdisposeします。

SingleChildScrollViewでフォームを囲むと、狭い画面やキーボード表示時に入力欄をスクロールできます。ブラウザーの高さを小さくし、登録ボタンまで移動できることも確認します。

05

練習:学習内容を20文字以内に

空欄だけでなく、学習内容が20文字を超えた場合もエラーにしてください。DartのString.lengthはUTF-16コード単位なので、画面上の文字数にはcharactersを使います。これはFlutterのmaterial.dartから利用できます。

解答例を確認する
学習内容のTextFormField.validatorを置き換えDART
validator: (value) {
  final title = value?.trim() ?? '';
  if (title.isEmpty) return '学習内容を入力してください';
  if (title.characters.length > 20) return '20文字以内で入力してください';
  return null;
},
画面で確認する結果OUTPUT
空欄 → 学習内容を入力してください
21文字 → 20文字以内で入力してください
Dart・20分 → Dartを20分学習しました

公式ドキュメントで確認する:フォームの検証 / 入力の変化を扱う