【Flutter】go_routerで「○秒後にリダイレクト」を表現してみた。

go_routerのGoRouterクラスにはredirectという関数があります。こちらの関数はナビゲーションイベントが発生する毎に発火し、条件にあった画面にリダイレクトさせることができます。


```dart
class GoRouterSample extends StatelessWidget {
  ...
  late final _router = GoRouter(
    redirect: (state) {
    // ここにリダイレクト先と条件を書いていく
    },
  );
}
```

今回はこのredirectを利用してwebページによくあるような「○秒後にリダイレクト」を表現してみました。

<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2466417/820ad699-bbb6-3f1a-8377-bed13b7fafad.gif' width='300'>

## 実装内容

秒数を管理するクラスを定義（ValueNotifier便利だな〜）

```count.dart
class Count extends ValueNotifier<int> {
  Count(super.value);

  void countDown() {
    value = value - 1;
  }

  void resetCount() {
    value = 5;
  }
}

```

main.dartにgo_routerの諸々を定義。アプリ起動時にget_itというパッケージでCountクラスをシングルトンとして登録しています。

```main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  // get_itを使用してCountクラスをシングルトンとして登録　　　　　　
  GetIt.I.registerSingleton(Count(5));

  runApp(
    GoRouterSample(),
  );
}

class GoRouterSample extends StatelessWidget {
  GoRouterSample({Key? key}) : super(key: key);
  // get_itに登録したCountクラスを取得　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　
  final countRepository = GetIt.I<Count>();

  @override
  Widget build(BuildContext context) => MaterialApp.router(
        routeInformationProvider: _router.routeInformationProvider,
        routeInformationParser: _router.routeInformationParser,
        routerDelegate: _router.routerDelegate,
      );

  late final _router = GoRouter(
    initialLocation: '/login',
    // ルート診断情報の出力を有効にする（なくても良い）
    debugLogDiagnostics: true,
    // Listenableクラスに変化があればルートがリフレッシュされる（redirectが発火する）
    refreshListenable: countRepository,
    routes: [
      GoRoute(
        path: '/login',
        builder: (context, state) => TopPage(),
      ),
      GoRoute(
        path: '/first',
        builder: (context, state) => FirstLoginPage(),
      ),
      GoRoute(
        path: '/home',
        builder: (context, state) => HomePage(),
      ),
    ],
    // ナビゲーションイベントが起こるたびに発火
    redirect: (state) {
      // countが0になったらcountをリセットし、ホーム画面にリダイレクト
      // (countを0にしないと無限ループしてしまうため)
      if (countRepository.value == 0) {
        countRepository.resetCount();
        return '/home';
      }
      // nullを返すと本来の遷移先に遷移してくれる
      return null;
    },
  );
}
```

GoRouterコンストラクタのrefreshListenableプロパティに監視したいクラスを指定します（ChangeNotifierやValueNotifer等）。指定したクラス（正しくは状態変数？）に変更があった場合はルートをリフレッシュしてくれます（つまりredirectを発火してくれる）

今回はこれを活用しValueNotifier継承クラスでint型の秒数を管理。1秒ごとに-1して状態変数を更新。ValueNotifier継承クラスが更新されたためredirectが発火され、もし秒数が0の場合はホーム画面にリダイレクト。といった形で実装しています。


FirstLoginPageでは`Timer`を使用して1秒ごとに状態変数を-1するように実装しています。
```first_login.dart
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_playground/presentasion/pages/playground/go_router/main.dart';
import 'package:get_it/get_it.dart';

class FirstLoginPage extends StatefulWidget {
  const FirstLoginPage({Key? key}) : super(key: key);

  @override
  State<FirstLoginPage> createState() => _FirstLoginPageState();
}

class _FirstLoginPageState extends State<FirstLoginPage> {
  // 登録済みのCountクラスを取得
  final count = GetIt.I<Count>();
  late Timer _timer;

  @override
  void initState() {
    super.initState();
    _timer = Timer.periodic(
      const Duration(seconds: 1),
      (Timer timer) {
        // 1秒ごとにcount.valueを-1している
        count.countDown();
        setState(() {});
      },
    );
  }

  @override
  void dispose() {
    super.dispose();
    _timer.cancel();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('初回ログイン'),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                SizedBox(
                  child: Text(
                    count.value.toString(),
                  ),
                ),
                const Text('秒後にホーム画面に遷移します。'),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

```

（TopPageとHomePageは特段解説する必要はないと思うので省きます。）


今回は解説というよりは「こんなもの作ってみました〜」という感じなので具体的な説明は省きましたが、go_routerでリダイレクトができるってことは伝わったかなと思います。どなたかの参考になれば嬉しいです。

## 参考
https://gorouter.dev/

https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html

https://pub.dev/packages/get_it