Iganinのブログ

日頃の開発で学んだ知見を中心に記事を書いています。

【Flutter】Navigation2.0とAutoRouteライブラリを使用して、画面遷移時に認証状態を確認する

tl;dr;

  • 画面遷移時の認証状態をAutoRouteライブラリのAutoRouteGuardを使って確認する
    • 認証していない場合は認証画面に遷移させるなどもできる
  • riverpodでDIしている場合でも対応できる

書くこと

  • auto_route + riverpodでの画面遷移時の認証状態確認方法

書かないこと

  • auto_routeの詳しい使用方法
  • riverpodの詳しい使用方法

内容

アプリを作成していると画面遷移時に認証状態を確認し、認証していない場合は認証画面に遷移させる、もしくは認証画面を表示したい場合がある。

認証していなければ使用できないアプリケーションはもちろんだが、通常の画面は認証なしでも使用でき、購入画面以降では認証が必須というような場合もありえる。

以下で、auto_routeとriverpodでそのような対応を行う方法を記載する。

※ Webアプリだとどの画面に遷移してくるかわからないため、上記対応は必須のはず。

auto_route

Flutterの画面遷移関連のライブラリ。 下記のように記載することで、 router.push(XxxRoute())router.pop()で画面遷移できる。とても便利。

@AdaptiveAutoRouter(
  replaceInRouteName: 'Page,Route',
  routes: <AutoRoute>[
    AutoRoute(path: '/', page: InitialPage, initial: true),
    AutoRoute(path: '/'sign_in, page: SignInPage),
    AutoRoute(path: '/sign_up', page: SignUpPage),
  ],
)
class $Router {}
class App extends HookWidget {
  
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
    ~~~~~~
        routeInformationParser: router.defaultRouteParser(),
        routerDelegate: router.delegate(),
   ~~~~~~
  }

画面遷移時に認証状態の確認処理を挟む

auto_routeに含まれている AutoRouteGuardを使用する。

class AuthGuard extends AutoRouteGuard {
  @override
  Future<void> onNavigation(
    NavigationResolver resolver,
    StackRouter router,
  ) async {
    final isAuthenticated = await _isAuthenticated();
    if (isAuthenticated) {
      resolver.next(true);
    } else {
      // 場合によってはここで再認証処理をかけて、結果次第で画面遷移をするかどうか決めると良さそう
      router.replaceAll([SignInRoute()]);
      resolver.next(false);
    }
  }
}

AutoRouteGuardを定義した上で、AdaptiveAutoRouterのAutoRouteのguards引数にGuardのクラスを記載すると、該当の画面に遷移する前にGuardのonNavigationが呼ばれるようになる。

@AdaptiveAutoRouter(
  replaceInRouteName: 'Page,Route',
  routes: <AutoRoute>[
    AutoRoute(path: '/', page: InitialPage, initial: true, guards: [AuthGuard]),
    AutoRoute(path: '/', page: SignInPage),
    AutoRoute(path: '/', page: SignUpPage),
  ],
)

class $Router {}

ここで、引数などをどのように渡すのかという疑問が湧くが、 AdaptiveAutoRouterから build_runnerで生成されたクラスに 使用しているguardを引数としたコンストラクタが自動生成されるみたい。 したがって上記のRouterを初期化しようとすると下記のようになる。

Router(authGuard: AuthGuard());

riverpodを組み込む

上記のようにRouterの初期化時にGuardを生成して渡すので、riverpodによるDIも容易にできる。

final authGuardProvider = Provider((ref) => AuthGuard(ref.read));

class AuthGuard extends AutoRouteGuard {
  AuthGuard(this._read);

  final Reader _read;
  AuthRepositoryBase get _authRepository => _read(authRepositoryProvider);
  ~~~~
}
class App extends HookWidget {
  App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final authGuard = useProvider(authGuardProvider);

    final router = router(authGuard: authGuard);
    ~~~~
   }

    ~~~~
}

その他

  • guardを認証状態の確認に使用するのは、auto_routeのREADMEに記載もあったので推奨の方法なのだと思う。
  • guards: [] の形で複数渡せるので、 一覧 - 詳細 の画面があった際に 詳細画面に遷移したときに必要な情報がないから一覧に遷移させる、といったこともできそう。

参考