👓

Flutter TextFieldが勝手にフォーカスされる問題

に公開

問題の概要

症状: Drawerを開いた瞬間に、TextFieldが自動的にフォーカスを獲得してキーボードが意図せず表示される
※Drawerに限らず、他のアクションでも自動的にフォーカスされる。

再現手順:

  1. TextFieldをタップしてキーボードを表示
  2. キーボードの閉じるボタンでキーボードを閉じる
  3. Drawerを開く
  4. 意図しない結果: TextFieldが再度フォーカスを獲得し、キーボードが表示される

イメージ

コード

KeyboardToolbar( 
    onDismiss: () { //キーボードの閉じるボタンを押すた時に時の処理
      _isKeyboardVisible = false;
      FocusScope.of(context).unfocus();
  }
)

根本原因の解明

真の敵:Flutter のFocusScope/FocusManager履歴機能

Flutterのフォーカス管理システムには「履歴機能」があり、以前フォーカスを持っていたウィジェットを記憶している。Drawer等のモーダル遷移後、システムが自動的に以前のフォーカス状態を復元しようとする。キーボード操作などアクセシビリティの一貫性のためだと思われる。

Within a scope, the most recent nodes to have focus are remembered, and if a node is focused and then removed, the original node receives focus again.
FocusScopeNode class

// 問題のメカニズム
// 1. TextFieldがフォーカスを持つ → FocusManagerが履歴に記録
// 2. unfocus()でフォーカスを外す → しかし履歴は残る
// 3. Drawerを開く → モーダル遷移が発生
// 4. システムが履歴からTextFieldのフォーカスを復元 ← ここが問題!

フォーカス履歴リセット作戦

解決策

KeyboardToolbar( 
    onDismiss: () {
      _isKeyboardVisible = false;
      FocusScope.of(context).requestFocus(FocusNode()); //追加!
      FocusScope.of(context).unfocus();
  }
)

解決メカニズム

// 新しいFocusNodeにフォーカスを移す
FocusScope.of(context).requestFocus(FocusNode());

// 効果:
// 1. 以前のTextField履歴が上書きされる
// 2. Drawer開閉時にTextFieldフォーカス復元が発生しない

解決🎊

Discussion