docs

TOP(About this memo)) > 一覧(Flutter) > go_router

go_router

(IMO) 名称について

アップデート頻度

主なプロパティ

GoRouter
  initialLocation: String? initialLocation
  redirect: FutureOr<String?> Function(BuildContext, GoRouterState)?
  refreshListenable: Listenable? 
  navigatorKey: GlobalKey<NavigatorState>?
  onException: void Function(BuildContext, GoRouterState, GoRouter)?
  debugLogDiagnostics: bool
  routes: [
    GoRoute
      path: String
      name: String
      builder: 
          Widget Function(
            BuildContext context,
            GoRouterState state,
        )
      routes: <RouteBase>[] 
    ...
    ShellRoute
      builder: Widget Function(
            BuildContext context,
            GoRouterState state,
            Widget child,
        ),
      branches: <RouteBase>[
        GoRoute
      ]
  ]

GoRouter.go と GoRouter.push メソッド

機能 メソッド 動作
go GoRouter.go(…) または BuildContext.go(…) 親ルートからサブルートへ移動したときのみスタックに積まれる。
実行前のスタックは全てリセットされる。
push GoRouter.push(…) または BuildContext.go(…) どのページへの移動でもスタックへ積まれる
push GoRouter.pop(…) または BuildContext.pop(…) スタックの先頭要素をpopする

(参考) (例) StatefulShellRouteを使った ボトムナビゲーション(+ドローワー)を含む画面の構成の実現

(参考) (例) フルスクリーン かつ ナビゲーションスタックに積むページ

実現方法

エラーハンドリング

機能 原因 ハンドリングの例
GoError/AssertionError GoRouterが正しく使用されていない(ソースコードの修正が必要) main関数でキャッチしてスタックトレース
GoException ルートが存在しない場合 GoRouter.onExceptionでハンドリングしてエラー画面を表示
void main() {
  testWidgets('', (WidgetTester tester) async {
    // Build our app and trigger a frame.
    await tester.pumpWidget(MaterialApp.router(
      routerConfig: GoRouter(
        routes: [
          GoRoute(
            path: '/',
            builder: (context, state) => const Text("home"),
          ),
          GoRoute(
            path: '/404',
            builder: (context, state) => const Text("not found"),
          )
        ],
        onException:
            (BuildContext context, GoRouterState state, GoRouter router) {
          router.go('/404', extra: state.uri.toString());
        },
      ),
    ));

    await tester.pump();
    expect(find.text("home"), findsOne);
    tester.element(find.text("home")).go("/not/exist/route");
    await tester.pumpAndSettle();
    expect(find.text("not found"), findsOne);
    expect(find.text("home"), findsNothing);
  });
}

実行されるビルダー

ディープリンク

ディープリンクの遷移はスタックがリセットされる

NavigatorObserver

optionURLReflectsImperativeAPIs

go_router_builder

(参考)ディープリンクでCustom URL Schemesを渡した際のエラー

(参考)Issue

refreshとpopが同じフレームで実行されるとpopが実行されない

GoRouter.of(context).pop();// このpopが実行されない。
GoRouter.of(context).refresh();
GoRouter.of(context).pop();
await tester.pumpAndSettle(); //別のフレームで実行するとpopが動作する
GoRouter.of(context).refresh();
GoRouter.of(context).pop();
await Future.delayed(const Duration(milliseconds: 601));
GoRouter.of(context).refresh();

(参考) GoRouterのクラスの構成

GoRouter.redirect()内でGoRouter.of(context)はエラーとなる

void main() {
  testWidgets("GoRouter", (tester) async {
    await tester.pumpWidget(MaterialApp.router(
      routerConfig: GoRouter(
        navigatorKey: rootNavigatorKey,
        redirect: (context, state) {
          GoRouter.of(context);
          return null;
        },
        routes: [],
      ),
    ));
    await tester.pump();
  });
}
/*
...
No GoRouter found in context
...
When the exception was thrown, this was the stack:
#2      GoRouter.of (package:go_router/src/router.dart:507:12)
#3      main.<anonymous closure>.<anonymous closure> (file:///〜/flutter_application_12/test/widget_test.dart:14:20)
#4      RouteConfiguration.redirect.processRedirect (package:go_router/src/configuration.dart:417:80)
#5      RouteConfiguration.redirect (package:go_router/src/configuration.dart:429:14)
#6      GoRouteInformationParser._redirect (package:go_router/src/parser.dart:169:10)
#7      GoRouteInformationParser.parseRouteInformationWithDependencies (package:go_router/src/parser.dart:104:32)
#8      _RouterState._processRouteInformation (package:flutter/src/widgets/router.dart:741:8) 
...
*/