`Flutter`のルーティング管理パッケージ`go_router`の使い方

`Flutter`のルーティング管理パッケージ`go_router`について以下の内容について記述します。

* インストール方法
* 基本的な使い方
* 応用的な使い方
    * 階層構造を持ったページを作りたい場合
    * ページ遷移時にパラメータを渡したい
    * エラー検知してエラーページを表示したい場合

https://github.com/flutter/packages/tree/main/packages/go_router

# パッケージのインストール

`pubspec.yaml`の`dependencies`に`go_router`を追加します。

```diff:pubspec.yaml
dependencies:
+  go_router:
```

`flutter pub get`でインストールします

```bash
$ flutter pub get
```

# 基本的な使い方
## アプリケーションでの初期設定
`lib/main.dart`でアプリケーションの`router`に`go_router`を使用するように設定します

```diff:main.dart
+ import 'package:go_router/go_router.dart';

class App extends StatelessWidget {
  App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
+      routerDelegate: _router.routerDelegate,
+      routeInformationParser: _router.routeInformationParser,
+      routeInformationProvider: _router.routeInformationProvider,
    );
  }

+ final _router = GoRouter();
}
```

具体的な値としては`GoRouter()`の中にルーティングを追加していきます。

## `GoRouter()`の書き方

`GoRouter()`の書き方は以下の通りです。

```dart
GoRouter(
  name: 'top',
  path: '/',
  pageBuilder: (context, state) => MaterialPage(
        key: state.pageKey,
        child: Top(),
      ),
)
```

* `path`にルーティングのパスを書きます
* `name`にルーティングの名前を書きます
  * ルーティングを使用する際にフルパスではなく名前を指定することができるため名前を定義しておくことで呼び出し処理を簡潔にすることができます
* `pageBuilder`でページ描画処理を行います
  * 特別な処理を不要な場合`child`に`class`を指定するだけです

## 呼び出し方

ルーティングを呼び出したい`class`で`go_router`をimportして`context.go()`または`context.goNamed()`を使用します。

```dart
import 'package:go_router/go_router.dart';

context.go('/');
context.goNamed('top');
```

* `context.go()`を使用する場合は`path`で定義したルーティングのパスを引数に指定します
* `context.goNamed()`を使用する場合は`name`で定義したルーティングの名前を引数に指定します

# 応用的な使い方
## 階層構造を持ったページを作りたい場合

以下の画像のように左上に前のページに戻るボタンが設置されるような階層構造を作りたい場合

![スクリーンショット 2022-04-24 10.59.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184760/79825f4d-a219-b2c6-8456-cba2fa8b0bfd.png)

親ページにしたいルーティング定義で`routes`に子ページを配列で定義します。

```diff
GoRouter(
  name: 'top',
  path: '/',
  pageBuilder: (context, state) => MaterialPage(
        key: state.pageKey,
        child: Top(),
      ),
+  routes: [
+    GoRoute(
+      name: 'users',
+      path: 'users',
+      pageBuilder: (context, state) => MaterialPage(
+        key: state.pageKey,
+        child: Users(),
+    ),
+  ]
)
```

## ページ遷移時にパラメータを渡したい場合

前述の階層構造があるルーティングで親ページから子ページに遷移する際にパラメータを渡したい場合

1. ルーティングを使う側で`extra`引数に値を指定します。

```dart
context.goNamed('users', extra: 1);
```

2. `state.extra`に値が格納されるのでルーティングの定義の中で`child`の`class`に渡すなど使用します

```diff
GoRouter(
  name: 'top',
  path: '/',
  pageBuilder: (context, state) => MaterialPage(
        key: state.pageKey,
        child: Top(),
      ),
  routes: [
    GoRoute(
      name: 'users',
      path: 'users',
      pageBuilder: (context, state) => MaterialPage(
        key: state.pageKey,
        child: Users(
+          user_id: state.extra! as String
        ),
    ),
  ]
)
```

## エラー検知してエラーページを表示したい場合

`GoRouter()`の`errorPageBuilder`でエラーページの定義をすることで行います。
`state.error`にエラーオブジェクトが格納されます。

```diff
routes: [
    GoRouter(
      name: 'top',
      path: '/',
      pageBuilder: (context, state) => MaterialPage(
            key: state.pageKey,
            child: Top(),
          ),
],
+  errorPageBuilder: (context, state) => MaterialPage(
+    key: state.pageKey,
+    child: Scaffold(
+      body: Center(
+        child: Text(state.error.toString()),
+      ),
+    ),
+  ),
)
```

# ドキュメント

https://gorouter.dev/

# 環境

* Flutter 2.10.4
* Dart SDK version: 2.16.2