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
を使うのは正しいアプローチです。
ただし、それを「どこで使うか」で挙動が大きく変わる点には注意が必要!
同じようなハマりポイントに遭遇した方の参考になれば幸いです 🙌
🧑💻 ご質問やご意見があれば、コメントでお気軽にどうぞ!