Flutterで go_router を使っているとき、iOSでキーボードを表示すると無限リダイレクトが発生する現象に遭遇しました。
この記事ではその原因と、どのように解決したかを共有します!
🔥 問題の現象
theme: ThemeData(
navigationBarTheme: NavigationBarThemeData(
height: MediaQuery.of(context).size.height * 0.07,
),
),
このように MediaQuery を ThemeData 内で使用していたところ、
- iOSでキーボードを開いた瞬間
-
go_routerのredirect:が毎回実行される - 無限にリダイレクトが走り、ルーティングが止まる
という現象が発生しました。
🧠 なぜ起きたのか?
原因は「MediaQueryによるbuildの再実行」でした。
Flutterでは、MediaQuery, Theme, Directionality などの「InheritedWidget」に依存している場合、
その値が変わると自動的にWidgetツリーが再構築されます。
MediaQueryはキーボード表示・非表示などでsizeやviewInsetsが変化するため、
build()が再実行されるトリガーになります。
そして、MaterialApp.router() の中で GoRouter(...) を初期化していたため、
redirect: が毎回再評価 → 状況によっては無限ループが起こっていたわけです。
🛠 解決策:再構築の影響を最小限に抑える
✅ 解決策1:MediaQueryの使用箇所を下層に移す
MediaQuery は ThemeDataやGoRouterの初期化部分では使わず、
実際に必要なWidget内で使うようにしましょう。
NavigationBar(
height: MediaQuery.of(context).size.height * 0.07,
)
→ こうすれば、キーボードなどによる画面サイズの変化が発生しても、
NavigationBarだけが再構築され、GoRouter や ThemeData には影響しません。
✅ 解決策2:MediaQueryの値をキャッシュする
初回だけMediaQueryのサイズを取得して保存しておけば、
再構築のたびに毎回取得し直す必要はありません。
double? _screenHeight;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_screenHeight ??= MediaQuery.of(context).size.height;
}
✅ 解決策3:再構築を検知して明示的に制御する
Flutterの WidgetsBindingObserver を使えば、
画面サイズやキーボード表示の変化をイベントとして検知できます。
class _MyState extends State<MyPage> with WidgetsBindingObserver {
double _keyboardHeight = 0;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeMetrics() {
final bottomInset = WidgetsBinding.instance.window.viewInsets.bottom;
setState(() {
_keyboardHeight = bottomInset;
});
}
}
✅ まとめ
| ポイント | 説明 |
|---|---|
MediaQueryはInheritedWidget |
値が変わると依存Widgetのbuild()が再実行される |
GoRouterなどの初期化に影響を与えると危険 |
redirect: が毎回評価され無限ループになる |
| MediaQueryは必要な場所で、必要なタイミングだけ使おう | これが一番の安定策! |
💬 おわりに
レスポンシブ対応のために MediaQuery を使うのは正しいアプローチです。
ただし、それを「どこで使うか」で挙動が大きく変わる点には注意が必要!
同じようなハマりポイントに遭遇した方の参考になれば幸いです 🙌
🧑💻 ご質問やご意見があれば、コメントでお気軽にどうぞ!