Flutterの画面遷移は以下の2種類があります。
- Navigator 1: 命令的
- Navigator 2: 宣言的
Navigator 1ではWEB対応が不十分でした。WEB対応のためにNavigator 2がリリースされましたが、通常の開発者には難しいと言われていました。そのため、簡易化されたバージョンがリリースされるのではないかと言われていましたが、GoRouterは公式?な簡易的な画面遷移のプラグインになりました。この投稿では、まず最低限はGoRouterが動作する方法を紹介しています。その後に、パラメータを使ったデータ渡しの方法を整理しました。
ソース
ソースは以下にあります
https://github.com/fluttersalon/gorouter/tree/master/lib
インストール
いつもの通り、以下のコマンドを実行して、pubspec.yamlに追加します
flutter pub add go_router
flutter pub get
ファイルの作成
まず、以下の4つのファイルを作成します。
- main.dart
- router.dart
- page1.dart
- page2.dart
アプリを開始するための、いつものソースです。GoRouterを使うことを宣言しています
ページのpathとページのWidgetの対応づけをします。対応づけを一つのファイルですることで、ページが増えてきても、ソースが見通せるようにしています。(画面遷移の動きは、router.dartを見れば良くなる)
ページ1画面。初期のページです。初期ページの指定は、router.dartにあります。ボタンを押すことで、ページ2に移動します。
ページ2画面。遷移先のページです。ページのボタンか、バックボタンを押すことでページ1画面に戻ります。
router.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'page1.dart';
import 'page2.dart';
final router = GoRouter(
initialLocation: Page1.path,
routes: [
GoRoute(
path: Page1.path,
pageBuilder: (BuildContext context, GoRouterState? state) => MaterialPage(
child: Page1(),
),
),
GoRoute(
path: Page2.path,
pageBuilder: (BuildContext context, GoRouterState? state) => MaterialPage(
child: Page2(),
),
),
// ここに「GoRouterの追加分」を後で追加
],
);
GoRouterを定義して、ページのpathと対応するページのWidgetをGoRouteとして、定義していく。それぞれのページにstatic constでpathを定義する。追加のページが増えたら、新たなGoRouteをリストに追加する。
initialLocationに初期起動時に表示するページのpathを設定する。
main.dart
import 'package:flutter/material.dart';
import 'router.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Flutter Demo',
routerConfig: router,
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
}
}
- MaterialApp を MaterialApp.routerに変更
- home を 削除
routerDelegate: router.routerDelegateを追加(10.0.0では、routerConfigのみでOK)routeInformationParser: router.routeInformationParserを追加(10.0.0では、routerConfigのみでOK)- routerConfig: routerを追加
page1.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'page2.dart';
class Page1 extends StatefulWidget {
const Page1({Key? key}) : super(key: key);
static const path = '/page1';
@override
_Page1State createState() => _Page1State();
}
class _Page1State extends State<Page1> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page1'),
),
body: Column(children:[
TextButton(
onPressed: () => GoRouter.of(context).push(Page2.path),
child: Text('Go to page2'),
),
// ここに、「遷移元のデータ追加分」を追加
]),
);
}
}
- ページにstatic constのpathを付ける
- GoRouter.of(context).push(path)で遷移先を指定する
この場合、pushなので、backボタンでも戻れる。GoRouter.of(context).go(path) を使えば、backボタンでは戻れなくできる。
page2.dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class Page2 extends StatefulWidget {
const Page2({Key? key}) : super(key: key);
static const path = '/page2';
@override
_Page2State createState() => _Page2State();
}
class _Page2State extends State<Page2> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page2'),
),
body: TextButton(
onPressed: () => GoRouter.of(context).pop(),
child: Text('back'),
),
);
}
}
- GoRouter.of(context).pop() で元のページの戻る。
URLにパラメータを含める
実際に画面遷移に使おうとすると、IDなどを一緒にデータを渡すものです。そちらのやり方を記載いたします。
表示するページ
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class Page3 extends StatefulWidget {
Page3(this.mode, {this.id, Key? key}) : super(key: key);
static const path = '/page3/:mode/:id';
static const pathWithoutId = '/page3/:mode';
final String mode;
final String? id;
@override
_Page3State createState() => _Page3State();
}
class _Page3State extends State<Page3> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Page3 [mode=${widget.mode} id=${widget.id ?? 'no id'}]'),
),
body: TextButton(
onPressed: () => GoRouter.of(context).pop(),
child: Text('back'),
),
);
}
}
- path, pathWithoutIDでパスを定義しています。
- パスの中に「:(変数名)」とすることで、パラメータとしてデータを受け取ることができます
- pathは「/page3/update/1」という感じで、データのIDと処理名をpathに含めています。
- pathNoIdは「/page3/create」のように、処理名だけで、新規作成時などデータにIDがない状態を想定してます。
- pathNoIdで来たとき、idは渡されないので、Nullableにしてます。
- pathの定義をページのクラス内で定義してますが、パラメータがあるとGoRouteのあるファイルに入れた方が見通しが良いかも知れません
- コンストラクタ経由で、GoRouterから指定の値を受け取ります
- 受け取ったパラメータの値は、特にすることがないので、とりあえず表示してます。それぞれで処理してください
遷移元のデータ追加分
TextButton(
onPressed: () => GoRouter.of(context).push('/page3/update/1'),
child: Text('Go to page3 mode=update id=1'),
),
TextButton(
onPressed: () => GoRouter.of(context).push('/page3/new'),
child: Text('Go to page3 mode=new'),
),
- 「/page3/update/1」「/page3/new」という感じでパスにパラメータを含めて指定する(REST API風)
- updateや1は変数にして、実装ごとに変更する
GoRouterの追加分
新しいページを追加したので、それに対応するGoRouteをrouter.dartに追加します・
import 'page3.dart';
GoRoute(
path: Page3.path,
builder: (BuildContext context, GoRouterState state) {
final mode = state.params['mode']!;
final id = state.params['id'];
return Page3(mode, id: id);
},
),
GoRoute(
path: Page3.pathWithoutId,
builder: (BuildContext context, GoRouterState state) {
final mode = state.params['mode']!;
return Page3(mode);
},
),
- 対応するパス分、GoRouteを追加する(今回はpage3のデータIDがあるケースとないケースの2つあるから、2つ追加)
- state.params[パラメータ名]でパラメータをString?で取得できる
- modeは、絶対にデータが入っている想定のため「!」をつけることで、Nullだった場合は例外を発生させる
- idはNullかもしれないので、何もつけない
豆知識
flutter runでパスを指定して実行
flutter run --route=/page3/hoge/100
とすると、直接page3をパラメータ付きで開くことができる。iOSとAndroidでサポートされている。
多言語対応
多言語対応させるには、通常MaterialApp内に設定する。GoRouterの場合、 MaterialApp.router内で設定する。
return MaterialApp(
home: MaterialApp.router(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
),
多言語化対応については、以下でまとめています。参考にしてください。
トラブルシューティング
実行時にrouteInformation.state != null が発生した
以下のrouterConfigを確認してください。以前の書き方のままで実行すると、上記のエラーが発生しました。MaterialApp.router(
routerConfig: router,
)
まとめ
簡単に画面遷移ができるようになる。ただ、Navigation2で「宣言的に画面を遷移する!」と言っていたが、すっかり命令的な画面遷移になっている。良いのだろうか。
参考
本家
本家のドキュメント
本家のドキュメントの日本語訳
monoさんのつぶやき(flutter run -route)