Historical source · 2021–2022
日本語アーカイブ · Navigator との併用
原文・コード例・画像を保存した2021–2022年のドキュメント。
On this page
Navigator との併用
go_router は Navigator ウィジェットとの併用も可能です。 以下はこちらのサンプルからの一部抜粋です。
Future<void> _addPerson(BuildContext context) async {
final person = await Navigator.push<Person>(
context,
MaterialPageRoute(
builder: (context) => NewPersonScreen(family: widget.family),
),
);
if (person != null) {
setState(() => widget.family.people.add(person));
context.goNamed('person', params: {
'fid': widget.family.id,
'pid': person.id,
});
}
}このサンプルコードでは、まず Navigator を使って NewPersonScreen のページをスタックにプッシュし、AppBar に戻るボタンを表示させています。 そして Navigator.pop を使ってページの結果を元のページに戻しています。 GoRouter を使用していませんが、問題なく動作します。以下は NewPersonScreen のコードを一部抜粋したものです。
ButtonBar(children: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
final person = Person(
id: 'p${widget.family.people.length + 1}',
name: _nameController.text,
age: int.parse(_ageController.text),
);
Navigator.pop(context, person);
}
},
child: const Text('Create'),
),
]),この例のように GoRouter と Navigator を組み合わせて使っていただいても問題ありません。 ただ、Navigator.push を使った場合はブラウザのアドレスの更新ができません。 アドレスを更新する場合は、GoRouter.go か GoRouter.push を使う必要があります。 ところが、go も push も行った先のページの結果を受け取ることができません。 それは GoRouter が、ナビゲーションにディープリンクと動的リンクを組み込む形で使用されることを想定しているからです。 このことはアプリのロケーションに関することは全てリンクとして表現できるという点にも表れています。 Navigator.push でその場限りのページを生成し、結果を受け取るというようなことはリンクで表現できるものではありません。
とはいえ、GoRouter で同様のことができないわけではありません。実際、これは GoRouter が想定していたよくある事例です。 新規画面を呼び出す側がナビゲーションの結果を受け取ることができないのであれば、新規画面側でユーザーによる入力を処理すればいいのです。
ButtonBar(children: [
TextButton(
onPressed: () =>
// ここは Navigator.pop(context) や context.pop() でも可能
context.goNamed('family', params: {'fid': widget.family.id}),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
final person = Person(
id: 'p${widget.family.people.length + 1}',
name: _nameController.text,
age: int.parse(_ageController.text),
);
widget.family.people.add(person);
context.goNamed('person', params: {
'fid': widget.family.id,
'pid': person.id,
});
}
},
child: const Text('Create'),
),
]),この実装により、新規 person 追加用のページを生成するサブルートを設定できるようになりました。
GoRoute(
name: 'home',
path: '/',
builder: ...,
routes: [
GoRoute(
name: 'family',
path: 'family/:fid',
builder: ...,
routes: [
GoRoute(
name: 'person',
path: 'person/:pid',
builder: ...,
),
GoRoute(
name: 'new-person',
path: 'new-person',
builder: (context, state) {
final family = Families.family(state.params['fid']!);
return NewPersonScreen(family: family);
},
),
],
),
],
),結果、_addPerson メソッドは次のようになります。
void _addPerson(BuildContext context) {
context.goNamed('new-person', params: {'fid': widget.family.id});
}最後に、アプリの様々な場所で使われるデータに変更を加える場合、その状態に依存するウィジェットが確実に再構築されるよう気を配る必要があります。 (例えば、Family の Person オブジェクトのリストに変更を加えるページとは別に、その内容を表示するページがある場合など) これには provider などの状態管理ソリューションを使用することをおすすめします。
GoRouter と Navigator を一緒に使ってはいけない理由はありません。 ただ、アプリの状態をどのように管理していくか、Navigator と GoRouter をそれぞれ利用する意義についてはよく考える必要があります。
Sources & version context
GoRouter.dev documentation edition. The preserved text describes the original package APIs. Follow the current guide before using it in a new project.