Flutterでルーティングの実装を行う「go_router」

# この記事について
- Flutterでルーティングの実装をする時に使用するgo_routerの使い方について紹介します
- [go_routerのdocs](https://pub.dev/documentation/go_router)
# 基本設定
- ホーム画面、設定画面、ユーザー一覧、詳細画面を追加する場合、下記のようにルーティングを設定します
```dart
import 'package:go_router/go_router.dart';

final GoRouter router = GoRouter(
  initialLocation: '/users',
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      name: 'home',
      builder: (BuildContext context, GoRouterState state) {
        return const Home();
      },
    ),
    GoRoute(
      path: '/settings',
      name: 'settings',
      builder: (BuildContext context, GoRouterState state) {
        return const Setting();
      },
    ),
    GoRoute(
        path: '/users',
        name: 'users',
        builder: (BuildContext context, GoRouterState state) {
          return const Users();
        },
        routes: [
          GoRoute(
            path: ':id',
            name: 'userDetail',
            builder: (BuildContext context, GoRouterState state) {
              final String userId = state.pathParameters['id']!;
              return UserDetail(userId: userId);
            },
          )
        ]),
  ],
);
```
- まずルーティングの設定は`GoRouter`の`routes`の中に設定したいルーティングを記載します
```dart
GoRouter(
  routes: [
    // ここにルーティング情報を記載する
  ]
);
```
- アプリ初回表示時に遷移したい画面がある場合は`initialLocation`に指定します。
```dart
final GoRouter router = GoRouter(
  initialLocation: '/users',
  // ...
);
```
- ユーザー一覧画面、ユーザー詳細画面のように画面構成をネストにしたい場合は親のユーザー一覧画面の`routes`にユーザー詳細画面のルーティングを設定します
```dart
    GoRoute(
        path: '/users',
        name: 'users',
        builder: (BuildContext context, GoRouterState state) {
          return const Users();
        },
        routes: [
          // ここに子ページのルーティングを記載する
          GoRoute(
            path: ':id',
            name: 'userDetail',
            builder: (BuildContext context, GoRouterState state) {
              final String userId = state.pathParameters['id']!;
              return UserDetail(userId: userId);
            },
          )
        ]),
```
- ダイナミックルーティングを使用する場合は下記のように`path`を指定します。今回は`id`を受け取りたいので`:id`としました。そして`builder`内でパスパラメータから`id`を取得し、それを`UserDetail`画面に渡します
```dart
  GoRoute(
    path: ':id',
    name: 'userDetail',
    builder: (BuildContext context, GoRouterState state) {
      final String userId = state.pathParameters['id']!;
      return UserDetail(userId: userId);
    },
  )
```
# 画面遷移について
- 各画面から別の画面に遷移する方法は下記の通り`context.go()`で行います
```dart
    TextButton(
        onPressed: () => context.go('/settings'),
        child: const Text('go to settings page')),
    TextButton(
        onPressed: () => context.go("/users"),
        child: const Text('go to users page')),
    TextButton(
        onPressed: () => context.go("/users/123"),
        child: const Text('go to user detail page'))
```
- また、画面間の値の受け渡しについては`context.pop`メソッドを使用して、遷移先の画面で値を返し、`context.push`メソッドを使用して、非同期的に画面遷移を行い、遷移元の画面で値を受け取ります


# リダイレクト
## トップレベルリダイレクト
- ルーティング全体に対して適用されるリダイレクト。ユーザーのログイン情報に基づいてログイン画面にリダイレクトするときなどに使用
## ルートレベルリダイレクト
- 特定の条件の時に適用するリダイレクト。特定の条件に合致する場合、その固有の条件に応じてリダイレクトを行う
# トランジションアニメーション
- トランジションアニメーションは画面描画間に任意のアニメーションを表示する機能です。試しに`page_with_animation`というページを用意してそのページに遷移する際にアニメーションを施してみました
```dart
    GoRoute(
      path: "/page_with_animation",
      name: "page_with_animation",
      pageBuilder: (context, state) {
        return CustomTransitionPage(
          key: state.pageKey,
          child: const PageWithAnimation(),
          transitionsBuilder: (context, animation, secondaryAnimation, child) {
            return FadeTransition(
              opacity:
                  CurveTween(curve: Curves.easeInOutCirc).animate(animation),
              child: child,
            );
          },
        );
      },
    ),
```
![画面収録-2024-06-22-14.09.48.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3811583/49c07985-3406-2bab-20df-f9c92b3e7928.gif)

# 型安全なルート
- 現在寄稿中...
# 名前付きルート
- 名前付きルートはルーティングに名前を付与することで各画面から別の画面遷移を行う際に、パスで遷移するのではなく、名前指定することで画面遷移が可能になるというものです。基本設定のところで示したコードの`name: 'home',`の部分にあたります
```dart
    GoRoute(
      path: '/',
      name: 'home',
      builder: (BuildContext context, GoRouterState state) {
        return const Home();
      },
    ),
```
- ホーム画面に画面遷移する場合は`context.goNamed('home')`と記述します
# エラーハンドリング
- エラーハンドリング方法としては`errorBuilder`をルーティング内に記載することでエラー発生時のハンドリングが可能になります。下記の例ではエラー発生時に`ErrorScreen`画面に遷移するようになっています
```dart
GoRouter(
  errorBuilder: (BuildContext context, GoRouterState state) {
    return ErrorScreen(error: state.error.toString());
  },
);
```