概要
ログインしていない状態で、ユーザー情報を必要とする画面への遷移
のように、特定条件下で、特定の画面に遷移することを中断したいケースがあると思います。
これをGoRouter
で実現する方法を考えます。
環境
- Flutter 3.19.6
- Dart 3.3.4
- GoRouter 13.2.4
結論
redirect
で特定条件を満たしているか判断し、
満たしていれば、存在しないパスを返し、例外を引き起こす。
onException
で例外を受け取り、state
からパスを取得し、パスごとの処理を行う。
GoRouter(
redirect: (context, state) {
final isLoggedIn = ...;
final fullPath = state.fullPath;
if (!isLoggedIn && fullPath == '/required_logged_in_page') {
return '/not_logged_in'; // 存在しないパス
}
return null; // 遷移を許可する
},
onException: (context, state, exception) {
if (state.uri.toString() == '/not_logged_in') {
// 必要に応じて、ダイアログなどを表示する
}
},
...
);
考え方
特定条件下で画面遷移を中断したい
ということで、画面遷移中に処理を挟むことを考えます。
[失敗] NavigatorObserver
を用いる
NavigatorObserverはFlutter標準の仕組みとして用意されている、遷移時に処理を挟むことができる仕組みです。
これはGoRouter
でもサポートされています。
しかし、用意されているタイミングとしては、
didPop
didPush
didRemove
didReplace
などのように、処理後になるので、遷移を中断する、という用途には利用できませんでした。
[失敗] GoRoute
のbuilder
で例外をスローする
GoRouter
にはどうやら例外をキャッチするonException
という仕組みがありそうでした。
ということで、ページを構築する箇所で例外をスローすることで対応してみようとします。
GoRouter(
routes:[
GoRoute(
path: '/required_logged_in_page',
builder: (context, state) {
final isLoggedIn = ...;
if (!isLoggedIn) {
throw NotLoggedInException();
}
return const RequiredLoggedInPage();
},
),
...
],
onException: (context, state, exception) {
if (exception is NotLoggedInException) {
// 必要に応じて、ダイアログなどを表示する
}
},
...
);
class NotLoggedInException extends Exception {}
この場合だと、onException
がキャッチしてくれず、思った挙動を得られませんでした。
[調査] GoRouter
のredirect
について
少し調べると、redirect
は特定条件下で、他のページへ遷移するために使われることが分かりました。
GoRouter(
redirect: (context, state) {
final isLoggedIn = ...;
final fullPath = state.fullPath;
if (!isLoggedIn && fullPath == '/required_logged_in_page') {
// ここで遷移を中断したい
}
return null; // 遷移を許可する
},
...
),
文字列を返すと、そのパスへのリダイレクト遷移を行え、
nullを返すと、そのまま遷移を実行する。
というものでした。
[成功] GoRouter
のredirect
で存在しないパスを返す
先程のonException
とredirect
のあわせ技として、
redirect
が存在しないパスを返せば、onException
で受け取れるのでは?
という仮説のもと、実装してみた結果、想定した挙動が得られました。
再掲となりますが、こちらが成功時のコードのサンプルです。
GoRouter(
redirect: (context, state) {
final isLoggedIn = ...;
final fullPath = state.fullPath;
if (!isLoggedIn && fullPath == '/required_logged_in_page') {
return '/not_logged_in'; // 存在しないパス
}
return null; // 遷移を許可する
},
onException: (context, state, exception) {
if (state.uri.toString() == '/not_logged_in') {
// 必要に応じて、ダイアログなどを表示する
}
},
...
);
まとめ
ちょっとハック的にはなりますが、
redirect
で存在しないパスを返し、onException
で受け取る
という方法で遷移を中断することができました。
今回はログイン状態かどうか、というケースでしたが、ほかケースにも応用できるかと思うので、ぜひ。