TextFormFieldにListenerをAddして処理する – Flutter

[PR]

入力フォーム2つ目。

前回は明確にSubmitしてもらうパターンだったけど、今回は随時更新するパターン。
例えば、ユーザーがプロフィール情報をアップデートするときに、入力したら自然と更新されてるみたいにするやつ。

で、やってみたものの、懸念点がいくつがあり。
とりあえずコード。

参考
https://stackoverflow.com/questions/48870082/managing-events-in-flutters-textformfield

import 'package:flutter/material.dart';

class FormListenerPage extends StatefulWidget {
  @override
  State createState() => _FormListenerPageState();
}

class _FormListenerPageState extends State {
  String _lastSubmitValue = "初期値";
  TextEditingController _textEditingController;
  FocusNode _focusNode;

  void _onChange() {
    final _editingText = _textEditingController.text;
    final _hasFocus = _focusNode.hasFocus;
//    debugPrint("onChange: $_editingText, hasFocus: $_hasFocus"); // 確認用

    // 変更があってフォーカスが外れた場合だけ実行(必要があればバリデートも)
    if (_editingText != _lastSubmitValue && !_hasFocus) {
      _lastSubmitValue = _editingText;
      debugPrint("実行: $_editingText");
    }
  }

  @override
  void initState() {
    super.initState();
    _textEditingController = TextEditingController(text: _lastSubmitValue)..addListener(_onChange);
    _focusNode = FocusNode()..addListener(_onChange);
  }

  @override
  void dispose() {
    _textEditingController.dispose();
    _focusNode.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('📮📮')),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: ListView(
          children: [
            TextFormField(
              decoration: InputDecoration(labelText: "ここをリッスン", border: OutlineInputBorder()),
              controller: _textEditingController,
              focusNode: _focusNode,
              autofocus: true,
//              onFieldSubmitted: onFieldSubmitted, // 無しでも_onChangeが動くからいらない
            ),
            SizedBox(height: 16.0),
            TextFormField(
              decoration: const InputDecoration(
                icon: Icon(Icons.beach_access),
                hintText: "上のTextFormFieldからフォーカス外し",
              ),
            ),
          ],
        ),
      ),
    );
  }
}

TextEditingControllerとFocusNode両方にイベント発生時の処理をセットして。
その処理の中で、それぞれの状態を確認して必要な処理を実行する感じです。

今回のケースではFocusNodeだけにセットで良いはずだけど、更新条件次第では必要だと思うので、備忘録的にTextEditingControllerにもセットしてます。

以下、懸念点など。

キーボードをバックボタンで消したときはフォーカスが外れないので、イベント自体は発行されるけど、通常の入力との使い分けができない。
方法はなにかしらあるかもだけど、そもそもキーボードで戻る = キャンセル的な意味合いだろうから、このタイミングでなにかを実行しようとするのが間違いなのかも。

画面自体のバック時にはフォーカスが外れるので実行されます。
Firestoreに保存するぐらいなら問題なく動いたけど、もっと長い処理を実行して結果をエラーダイアログを出すとかだと変なことになりそうな気もする。
そこも、構成によるとは思うけど。

といった感じで、ケースよにっては安心して使えないこともありそう。
onChange内の処理を調整するとか、一時保存的なケースでだけ使うとか。
しっかり考慮した方がよさそう。

それ以前にもっといい実装方法があるかもだけど。現時点ではこんな感じ。