HOME / BLOG
BLOG

Flutterでフォーム入力を管理する方法【TextEditingController・Form】

デフォルト画像

Flutterで入力フォームを作るときは、TextEditingController で入力値を扱い、FormTextFormField で検証する形にすると保守しやすくなります。

この記事では、選手メモのような小さな入力画面を例に、controllerの作成、dispose、validator、送信処理までをまとめます。

サンプルコード

import 'package:flutter/material.dart';

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

  @override
  State<PlayerMemoForm> createState() => _PlayerMemoFormState();
}

class _PlayerMemoFormState extends State<PlayerMemoForm> {
  final _formKey = GlobalKey<FormState>();
  final _nameController = TextEditingController();
  final _memoController = TextEditingController();

  @override
  void dispose() {
    _nameController.dispose();
    _memoController.dispose();
    super.dispose();
  }

  void _submit() {
    final isValid = _formKey.currentState?.validate() ?? false;

    if (!isValid) {
      return;
    }

    final name = _nameController.text.trim();
    final memo = _memoController.text.trim();

    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('$name のメモを保存しました')),
    );

    debugPrint('name: $name');
    debugPrint('memo: $memo');
  }

  @override
  Widget build(BuildContext context) {
    return Form(
      key: _formKey,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          TextFormField(
            controller: _nameController,
            decoration: const InputDecoration(
              labelText: '選手名',
              border: OutlineInputBorder(),
            ),
            validator: (value) {
              if (value == null || value.trim().isEmpty) {
                return '選手名を入力してください';
              }
              return null;
            },
          ),
          const SizedBox(height: 16),
          TextFormField(
            controller: _memoController,
            minLines: 3,
            maxLines: 5,
            decoration: const InputDecoration(
              labelText: 'メモ',
              border: OutlineInputBorder(),
            ),
            validator: (value) {
              if (value == null || value.trim().length < 5) {
                return '5文字以上で入力してください';
              }
              return null;
            },
          ),
          const SizedBox(height: 16),
          FilledButton(
            onPressed: _submit,
            child: const Text('保存する'),
          ),
        ],
      ),
    );
  }
}

実装の流れ

  1. GlobalKey<FormState> を作る
  2. TextEditingController を入力欄ごとに用意する
  3. TextFormFieldvalidator を書く
  4. 送信時に _formKey.currentState?.validate() を呼ぶ
  5. dispose() でcontrollerを破棄する

TextEditingControllerを使う理由

入力値を送信時に読むだけなら onSaved でも構いません。ただ、入力中の文字数表示、検索欄のクリア、初期値の反映、別Widgetとの連携が必要になると、TextEditingController を使った方が扱いやすくなります。

注意点

  • StatefulWidgetでcontrollerを作ったら、基本的に dispose() する
  • buildメソッド内で毎回controllerを作らない
  • 入力値は trim() してから保存やAPI送信に使う
  • フォームが大きくなったら、入力状態と保存処理を別クラスへ分ける

あわせて読みたい

参考資料