Flutter:release modeだと、アプリ起動時に、TextFieldがフォーカスされていても、キーボードが出現しない
結論:僅かな待ち時間を入れながら、フォーカスを一旦外して戻す
2022/4/14〜15 Flutter エラー・バグ日記
ここ数日、相当苦しんだ結果、ようやく解決したので記録。
アプリ起動と同時にすぐ書き込めるよう、TextFieldの「autofocus」を「true」にしていたが、Flutterをアップグレードしたためか、以前は問題なくできていた、アプリ起動と同時にキーボードを出す処理ができなくなっていた。
TextFieldはフォーカスされており、カーソルは点滅しているが、なぜかキーボードが出てこない。
この状態で、TextField内を1回タップすれば出てくる。
また、別画面にいったん遷移してから戻ってきた場合も、特にタップせずともキーボードが出てくる。
この現象は、release modeでだけ発生する。debug modeだと、ちゃんとキーボードが出る。逆であれば問題ないが、release modeで発生するとなると、見過ごせない。。
状態管理は「riverpod」を使っており、初め、この辺の自分の使い方が下手なためかと思ったが、試しにTextFieldしかないシンプルなコードを作っても、同じ状況だった。
以下に、試行錯誤した対応の流れをメモ。
対応①:「requestFocus」する→解決せず
ネット上でもかなり調べたが、大方の情報は、TextFieldの「focus」属性に、FocusNodeのインスタンス(例えば「focusNode」)を設置し、
- focusNode.requestFocus() もしくは、
- FocusScope.of(context).requestFocus(focusNode)
のメソッドを実行すればよい、というもの。
しかし、これをやっても変化はなかった。
対応②:「SystemChannels」を使う → 解決せず
こちらの情報に、「SystemChannels」を使った方法として、
SystemChannels.textInput.invokeMethod('TextInput.show');
というメソッドも紹介されていたため、試してみるも、やはり状況変わらず。
対応③:起動時に、いったんフォーカスを外し、再度フォーカス → 解決せず
こちらもよくネットで紹介されている
- FocusManager.instance.primaryFocus?.unfocus() もしくは、
- FocusScope.of(context).unfocus()
のメソッドを使い、アプリ起動時に、いったんフォーカスを外し、再度フォーカスするようにしてみたが、これも状況変わらず。
対応④:Timerクラスで待ってみる → 解決せず
フォーカス外し
→ Timerクラスを用いて1ミリ秒待つ
→ フォーカス当てる
という方法でうまく行った、という情報もあり、それも試してみるが、依然として駄目。。
対応⑤:起動時に、別Widgetにフォーカスを移してから戻す → 解決せず
こちらのイシュー情報で、いったん別のWidgetにフォーカスを当て、目的のWidgetにフォーカスを戻すと機能した、とあったので、やってみるが、これも変わらず。
対応⑥:待ち時間を入れながら、いったんフォーカスを移し、戻す → 解決!
対応④と⑤を組み合わせ、
Future.Delayedで200ミリ秒待つ
→ 別のFocusNodeにフォーカスを移す
→ Future.Delayedで200ミリ秒待つ
→ 元のFocusNodeにフォーカスを戻す
とやってみたところ、ようやく機能した(アプリ起動と同時に、キーボードを出現させられた)。
具体的なコードは以下。
initState内で、Widgetのビルドが終わってから実行する必要があるので、「WidgetsBinding.instance?.addPostFrameCallback」を使っている。
// 目的のWidget(TextFieldなど)に設置するFocusNode final _focusNodeO = FocusNode(); // ダミーのFocusNode。特にWidgetには配置しない final _focusNodeDummyO = FocusNode(); @override void initState() { WidgetsBinding.instance?.addPostFrameCallback((cb) async{ // Widget全体のビルドが終わったら、200ミリ秒待った後に、 // ダミーのFocusNode「_focusNodeDummyO」にフォーカスを移す await Future.delayed(Duration(milliseconds: 200)); FocusScope.of(context).requestFocus(_focusNodeDummyO); // 再度200ミリ秒待った後に、本来のFocusNode「_focusNodeO」にフォーカスを戻す await Future.delayed(Duration(milliseconds: 200)); FocusScope.of(context).requestFocus(_focusNodeO); }, ); super.initState(); }
ダミーのFocusNodeは、特にこれ自体を設置するWidgetを用意しなくてもエラーにならなかったので、FocusNodeのインスタンスのみ、用意すれば良い。
待つ時間は、100ミリ秒前後だと機能しなかった(機能したプロジェクトもあった、、、。この辺はコードの構造にも拠るのだろうか、、、)。
恐らく、release modeだと、処理速度が速いため、フォーカスオン→キーボード出現の処理が、順番どおり機能しないのかも?と推察(全然違っているかも知れませんが、、)。
なお、上記①〜⑤を試す過程で、releaseビルド初回は、アプリ起動と同時にキーボードが出るが、アプリを閉じて、再度起動させるとやっぱり出ない、という事が多かったので、初回ビルド時に機能しても安心できなかった。。
回りくどい方法で解決することになったが、引き続き調査して、もっとスマートな方法が見つかれば改善したい。
リリースしたアプリ(全てFlutterで開発)
個人アプリ開発で役立ったもの
おすすめの学習教材
\超初心者向けでオススメな元Udemyの講座/
\キャンペーン時を狙えば安価で網羅的な内容が学べる(日本語訳あり)/
\Gitの基礎について無料で学べる/
おすすめの学習書籍
\実用的。image_pickerに関してかなり助けられた/
\Dartの基礎文法を素早くインプットできる/