0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

特定条件下でGoRouterでの遷移を中断する

Last updated at Posted at 2024-05-15

概要

ログインしていない状態で、ユーザー情報を必要とする画面への遷移
のように、特定条件下で、特定の画面に遷移することを中断したいケースがあると思います。

これを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

などのように、処理後になるので、遷移を中断する、という用途には利用できませんでした。

[失敗] GoRoutebuilderで例外をスローする

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がキャッチしてくれず、思った挙動を得られませんでした。

[調査] GoRouterredirectについて

少し調べると、redirectは特定条件下で、他のページへ遷移するために使われることが分かりました。

redirectの調査
GoRouter(
  redirect: (context, state) {
    final isLoggedIn = ...;
    final fullPath = state.fullPath;
    if (!isLoggedIn && fullPath == '/required_logged_in_page') {
      // ここで遷移を中断したい
    }
    return null; // 遷移を許可する
  },
  ...
),

文字列を返すと、そのパスへのリダイレクト遷移を行え、
nullを返すと、そのまま遷移を実行する。
というものでした。

[成功] GoRouterredirectで存在しないパスを返す

先程のonExceptionredirectのあわせ技として、
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で受け取る
という方法で遷移を中断することができました。

今回はログイン状態かどうか、というケースでしたが、ほかケースにも応用できるかと思うので、ぜひ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?