[go_router] rewrite readme and examples (#2382)

diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md
index 465df98..86002df 100644
--- a/packages/go_router/CHANGELOG.md
+++ b/packages/go_router/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 4.2.4
+
+- Rewrites Readme and examples.
+
 ## 4.2.3
 
 - Fixes a bug where the ValueKey to be the same when a page was pushed multiple times.
diff --git a/packages/go_router/README.md b/packages/go_router/README.md
index 848425f..de139c8 100644
--- a/packages/go_router/README.md
+++ b/packages/go_router/README.md
@@ -1,13 +1,14 @@
-# Welcome to go_router!
+# go_router, A Declarative Routing Package for Flutter
 
-The purpose of [the go_router package](https://pub.dev/packages/go_router) is to
-use declarative routes to reduce complexity, regardless of the platform you're
-targeting (mobile, web, desktop), handle deep and dynamic linking from
-Android, iOS and the web, along with a number of other navigation-related
-scenarios, while still (hopefully) providing an easy-to-use developer
-experience.
+This package builds on top of the Flutter framework's Router API and provides
+convenient url-based APIs to navigate between different screens. You can
+define your own url patterns, navigating to different url, handle deep and
+dynamic linking, and a number of other navigation-related scenarios.
 
-You can get started with go_router with code as simple as this:
+## Getting Started
+
+Follow the [package install instructions](https://pub.dev/packages/go_router/install),
+and you can start using go_router in your code:
 
 ```dart
 class App extends StatelessWidget {
@@ -34,11 +35,102 @@
     ],
   );
 }
-
-class Page1Screen extends StatelessWidget {...}
-
-class Page2Screen extends StatelessWidget {...}
 ```
 
-# See [gorouter.dev](https://gorouter.dev) for go_router docs & samples
+## Define Routes
+
+go_router is governed by a set of routes which are specify as part of the
+[GoRouter](https://pub.dev/documentation/go_router/latest/go_router/GoRouter-class.html)
+constructor:
+
+```dart
+GoRouter(
+  routes: [
+    GoRoute(
+      path: '/',
+      builder: (context, state) => const Page1Screen(),
+    ),
+    GoRoute(
+      path: '/page2',
+      builder: (context, state) => const Page2Screen(),
+    ),
+  ],
+);
+```
+
+It defined two routes, `/` and `/page2`. Each route path will be matched against
+the location to which the user is navigating. The path will be matched in a
+case-insensitive way, although the case for parameters will be preserved. If
+there are multiple route matches, the <b>first</b> match in the list takes priority
+over the others.
+
+In addition to the path, each route will typically have a [builder](https://pub.dev/documentation/go_router/latest/go_router/GoRoute/builder.html)
+function that is responsible for building the `Widget` that is to take up the
+entire screen of the app. The default transition is used between pages
+depending on the app at the top of its widget tree, e.g. the use of `MaterialApp`
+will cause go_router to use the `MaterialPage` transitions. Consider using
+[pageBuilder](https://pub.dev/documentation/go_router/latest/go_router/GoRoute/pageBuilder.html)
+for custom `Page` class.
+
+## Initalization
+
+Once a [GoRouter](https://pub.dev/documentation/go_router/latest/go_router/GoRouter-class.html)
+object is created, it can be used to initialize `MaterialApp` or `CupertinoApp`.
+
+```dart
+final GoRouter _router = GoRouter(..);
+
+MaterialApp.router(
+  routeInformationProvider: _router.routeInformationProvider,
+  routeInformationParser: _router.routeInformationParser,
+  routerDelegate: _router.routerDelegate,
+);
+```
+
+## Error handling
+
+By default, go_router comes with default error screens for both `MaterialApp` and
+`CupertinoApp` as well as a default error screen in the case that none is used.
+Once can also replace the default error screen by using the [errorBuilder](https://pub.dev/documentation/go_router/latest/go_router/GoRouter/GoRouter.html):
+
+```dart
+GoRouter(
+  ...
+  errorBuilder: (context, state) => ErrorScreen(state.error),
+);
+```
+
+## Navigation
+
+To navigate between routes, use the [GoRouter.go](https://pub.dev/documentation/go_router/latest/go_router/GoRouter/go.html) method:
+
+```dart
+onTap: () => GoRouter.of(context).go('/page2')
+```
+
+go_router also provides a simplified means of navigation using Dart extension
+methods:
+
+```dart
+onTap: () => context.go('/page2')
+```
+
+<br>
+
+### Still not sure how to proceed? See [examples](https://github.com/flutter/packages/tree/main/packages/go_router/example) for complete runnable examples or visit [API documentation](https://pub.dev/documentation/go_router/latest/go_router/go_router-library.html)
+
+
+## Migration guides
+
+[Migrating to 2.0](https://flutter.dev/go/go-router-v2-breaking-changes)<br/>
+[Migrating to 2.5](https://flutter.dev/go/go-router-v2-5-breaking-changes)<br/>
+[Migrating to 3.0](https://flutter.dev/go/go-router-v3-breaking-changes)<br/>
+[Migrating to 4.0](https://flutter.dev/go/go-router-v4-breaking-changes)<br/>
+
+## Changelog
+
+See the [Changelog](https://github.com/flutter/packages/blob/main/packages/go_router/CHANGELOG.md)
+for a list of new features and breaking changes.
+
+
 
diff --git a/packages/go_router/example/README.md b/packages/go_router/example/README.md
new file mode 100644
index 0000000..21f6e80
--- /dev/null
+++ b/packages/go_router/example/README.md
@@ -0,0 +1,31 @@
+# Example Catalog
+
+## [Routes](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/main.dart)
+`flutter run lib/main.dart`
+
+An example to demonstrate a simple two-pages app.
+
+## [Sub-routes](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/sub_routes.dart)
+`flutter run lib/sub_routes.dart`
+
+An example to demonstrate an app with multi-level routing.
+
+## [Query parameters and path parameters](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/path_and_query_parameters.dart)
+`flutter run lib/path_and_query_parameters.dart`
+
+An example to demonstrate how to use path parameters and query parameters.
+
+## [Named routes](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/named_routes.dart)
+`flutter run lib/named_routes.dart`
+
+An example to demonstrate how to navigate using named locations instead of URLs.
+
+## [Redirection](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/redirection.dart)
+`flutter run lib/redirection.dart`
+
+An example to demonstrate how to use redirect to handle a sign-in flow.
+
+## [books app](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/books)
+`flutter run lib/books/main.dart`
+
+A fully fledged example that showcases various go_router APIs.
\ No newline at end of file
diff --git a/packages/go_router/example/lib/main.dart b/packages/go_router/example/lib/main.dart
index 91b827b..fab5a36 100644
--- a/packages/go_router/example/lib/main.dart
+++ b/packages/go_router/example/lib/main.dart
@@ -5,6 +5,15 @@
 import 'package:flutter/material.dart';
 import 'package:go_router/go_router.dart';
 
+// This scenario demonstrates a simple two-page app.
+//
+// The first route '/' is mapped to Page1Screen, and the second route '/page2'
+// is mapped to Page2Screen. To navigate between pages, press the buttons on the
+// pages.
+//
+// The onPress callbacks use context.go() to navigate to another page. This is
+// equivalent to entering url to the browser url bar directly.
+
 void main() => runApp(App());
 
 /// The main app.
@@ -29,11 +38,13 @@
         path: '/',
         builder: (BuildContext context, GoRouterState state) =>
             const Page1Screen(),
-      ),
-      GoRoute(
-        path: '/page2',
-        builder: (BuildContext context, GoRouterState state) =>
-            const Page2Screen(),
+        routes: <GoRoute>[
+          GoRoute(
+            path: 'page2',
+            builder: (BuildContext context, GoRouterState state) =>
+                const Page2Screen(),
+          ),
+        ],
       ),
     ],
   );
@@ -75,7 +86,7 @@
             children: <Widget>[
               ElevatedButton(
                 onPressed: () => context.go('/'),
-                child: const Text('Go to home page'),
+                child: const Text('Go back to home page'),
               ),
             ],
           ),
diff --git a/packages/go_router/example/lib/named_routes.dart b/packages/go_router/example/lib/named_routes.dart
index 859a4bc..ef58c52 100644
--- a/packages/go_router/example/lib/named_routes.dart
+++ b/packages/go_router/example/lib/named_routes.dart
@@ -4,10 +4,17 @@
 
 import 'package:flutter/material.dart';
 import 'package:go_router/go_router.dart';
-import 'package:provider/provider.dart';
 
 import 'shared/data.dart';
 
+// This scenario demonstrates how to navigate using named locations instead of
+// URLs.
+//
+// Instead of hardcoding the URI locations , you can also use the named
+// locations. To use this API, give a unique name to each GoRoute. The name can
+// then be used in context.namedLocation to be translate back to the actual URL
+// location.
+
 void main() => runApp(App());
 
 /// The main app.
@@ -15,21 +22,16 @@
   /// Creates an [App].
   App({Key? key}) : super(key: key);
 
-  final LoginInfo _loginInfo = LoginInfo();
-
   /// The title of the app.
   static const String title = 'GoRouter Example: Named Routes';
 
   @override
-  Widget build(BuildContext context) => ChangeNotifierProvider<LoginInfo>.value(
-        value: _loginInfo,
-        child: MaterialApp.router(
-          routeInformationProvider: _router.routeInformationProvider,
-          routeInformationParser: _router.routeInformationParser,
-          routerDelegate: _router.routerDelegate,
-          title: title,
-          debugShowCheckedModeBanner: false,
-        ),
+  Widget build(BuildContext context) => MaterialApp.router(
+        routeInformationProvider: _router.routeInformationProvider,
+        routeInformationParser: _router.routeInformationParser,
+        routerDelegate: _router.routerDelegate,
+        title: title,
+        debugShowCheckedModeBanner: false,
       );
 
   late final GoRouter _router = GoRouter(
@@ -62,47 +64,7 @@
           ),
         ],
       ),
-      GoRoute(
-        name: 'login',
-        path: '/login',
-        builder: (BuildContext context, GoRouterState state) =>
-            const LoginScreen(),
-      ),
     ],
-
-    // redirect to the login page if the user is not logged in
-    redirect: (GoRouterState state) {
-      // if the user is not logged in, they need to login
-      final bool loggedIn = _loginInfo.loggedIn;
-      final String loginloc = state.namedLocation('login');
-      final bool loggingIn = state.subloc == loginloc;
-
-      // bundle the location the user is coming from into a query parameter
-      final String homeloc = state.namedLocation('home');
-      final String fromloc = state.subloc == homeloc ? '' : state.subloc;
-      if (!loggedIn) {
-        return loggingIn
-            ? null
-            : state.namedLocation(
-                'login',
-                queryParams: <String, String>{
-                  if (fromloc.isNotEmpty) 'from': fromloc
-                },
-              );
-      }
-
-      // if the user is logged in, send them where they were going before (or
-      // home if they weren't going anywhere)
-      if (loggingIn) {
-        return state.queryParams['from'] ?? homeloc;
-      }
-
-      // no need to redirect at all
-      return null;
-    },
-
-    // changes on the listenable will cause the router to refresh it's route
-    refreshListenable: _loginInfo,
   );
 }
 
@@ -116,26 +78,17 @@
 
   @override
   Widget build(BuildContext context) {
-    final LoginInfo info = context.read<LoginInfo>();
-
     return Scaffold(
       appBar: AppBar(
         title: const Text(App.title),
-        actions: <Widget>[
-          IconButton(
-            onPressed: info.logout,
-            tooltip: 'Logout: ${info.userName}',
-            icon: const Icon(Icons.logout),
-          )
-        ],
       ),
       body: ListView(
         children: <Widget>[
           for (final Family f in families)
             ListTile(
               title: Text(f.name),
-              onTap: () => context
-                  .goNamed('family', params: <String, String>{'fid': f.id}),
+              onTap: () => context.go(context.namedLocation('family',
+                  params: <String, String>{'fid': f.id})),
             )
         ],
       ),
@@ -188,28 +141,3 @@
         body: Text('${person.name} ${family.name} is ${person.age} years old'),
       );
 }
-
-/// The login screen.
-class LoginScreen extends StatelessWidget {
-  /// Creates a [LoginScreen].
-  const LoginScreen({Key? key}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) => Scaffold(
-        appBar: AppBar(title: const Text(App.title)),
-        body: Center(
-          child: Column(
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: <Widget>[
-              ElevatedButton(
-                onPressed: () {
-                  // log a user in, letting all the listeners know
-                  context.read<LoginInfo>().login('test-user');
-                },
-                child: const Text('Login'),
-              ),
-            ],
-          ),
-        ),
-      );
-}
diff --git a/packages/go_router/example/lib/async_data.dart b/packages/go_router/example/lib/others/async_data.dart
similarity index 99%
rename from packages/go_router/example/lib/async_data.dart
rename to packages/go_router/example/lib/others/async_data.dart
index 5dfa003..fbe87e2 100644
--- a/packages/go_router/example/lib/async_data.dart
+++ b/packages/go_router/example/lib/others/async_data.dart
@@ -9,7 +9,7 @@
 import 'package:flutter/material.dart';
 import 'package:go_router/go_router.dart';
 
-import 'shared/data.dart';
+import '../shared/data.dart';
 
 void main() => runApp(App());
 
diff --git a/packages/go_router/example/lib/cupertino.dart b/packages/go_router/example/lib/others/cupertino.dart
similarity index 100%
rename from packages/go_router/example/lib/cupertino.dart
rename to packages/go_router/example/lib/others/cupertino.dart
diff --git a/packages/go_router/example/lib/error_screen.dart b/packages/go_router/example/lib/others/error_screen.dart
similarity index 100%
rename from packages/go_router/example/lib/error_screen.dart
rename to packages/go_router/example/lib/others/error_screen.dart
diff --git a/packages/go_router/example/lib/extra_param.dart b/packages/go_router/example/lib/others/extra_param.dart
similarity index 99%
rename from packages/go_router/example/lib/extra_param.dart
rename to packages/go_router/example/lib/others/extra_param.dart
index efab7be..c1624a0 100644
--- a/packages/go_router/example/lib/extra_param.dart
+++ b/packages/go_router/example/lib/others/extra_param.dart
@@ -6,7 +6,7 @@
 import 'package:flutter/material.dart';
 import 'package:go_router/go_router.dart';
 
-import 'shared/data.dart';
+import '../shared/data.dart';
 
 void main() => runApp(App());
 
diff --git a/packages/go_router/example/lib/init_loc.dart b/packages/go_router/example/lib/others/init_loc.dart
similarity index 100%
rename from packages/go_router/example/lib/init_loc.dart
rename to packages/go_router/example/lib/others/init_loc.dart
diff --git a/packages/go_router/example/lib/loading_page.dart b/packages/go_router/example/lib/others/loading_page.dart
similarity index 99%
rename from packages/go_router/example/lib/loading_page.dart
rename to packages/go_router/example/lib/others/loading_page.dart
index 9f61726..2c64389 100644
--- a/packages/go_router/example/lib/loading_page.dart
+++ b/packages/go_router/example/lib/others/loading_page.dart
@@ -6,7 +6,7 @@
 import 'package:go_router/go_router.dart';
 import 'package:provider/provider.dart';
 
-import 'shared/data.dart';
+import '../shared/data.dart';
 
 void main() => runApp(App());
 
diff --git a/packages/go_router/example/lib/nav_builder.dart b/packages/go_router/example/lib/others/nav_builder.dart
similarity index 99%
rename from packages/go_router/example/lib/nav_builder.dart
rename to packages/go_router/example/lib/others/nav_builder.dart
index 8ec6b07..5e80007 100644
--- a/packages/go_router/example/lib/nav_builder.dart
+++ b/packages/go_router/example/lib/others/nav_builder.dart
@@ -6,7 +6,7 @@
 import 'package:go_router/go_router.dart';
 import 'package:provider/provider.dart';
 
-import 'shared/data.dart';
+import '../shared/data.dart';
 
 void main() => runApp(App());
 
diff --git a/packages/go_router/example/lib/nav_observer.dart b/packages/go_router/example/lib/others/nav_observer.dart
similarity index 100%
rename from packages/go_router/example/lib/nav_observer.dart
rename to packages/go_router/example/lib/others/nav_observer.dart
diff --git a/packages/go_router/example/lib/nested_nav.dart b/packages/go_router/example/lib/others/nested_nav.dart
similarity index 99%
rename from packages/go_router/example/lib/nested_nav.dart
rename to packages/go_router/example/lib/others/nested_nav.dart
index 04b8285..049e418 100644
--- a/packages/go_router/example/lib/nested_nav.dart
+++ b/packages/go_router/example/lib/others/nested_nav.dart
@@ -5,7 +5,7 @@
 import 'package:flutter/material.dart';
 import 'package:go_router/go_router.dart';
 
-import 'shared/data.dart';
+import '../shared/data.dart';
 
 void main() => runApp(App());
 
diff --git a/packages/go_router/example/lib/push.dart b/packages/go_router/example/lib/others/push.dart
similarity index 100%
rename from packages/go_router/example/lib/push.dart
rename to packages/go_router/example/lib/others/push.dart
diff --git a/packages/go_router/example/lib/router_neglect.dart b/packages/go_router/example/lib/others/router_neglect.dart
similarity index 100%
rename from packages/go_router/example/lib/router_neglect.dart
rename to packages/go_router/example/lib/others/router_neglect.dart
diff --git a/packages/go_router/example/lib/router_stream_refresh.dart b/packages/go_router/example/lib/others/router_stream_refresh.dart
similarity index 99%
rename from packages/go_router/example/lib/router_stream_refresh.dart
rename to packages/go_router/example/lib/others/router_stream_refresh.dart
index 7a3ecf4..a042084 100644
--- a/packages/go_router/example/lib/router_stream_refresh.dart
+++ b/packages/go_router/example/lib/others/router_stream_refresh.dart
@@ -6,7 +6,7 @@
 import 'package:go_router/go_router.dart';
 import 'package:provider/provider.dart';
 
-import 'shared/data.dart';
+import '../shared/data.dart';
 
 void main() => runApp(const App());
 
diff --git a/packages/go_router/example/lib/shared_scaffold.dart b/packages/go_router/example/lib/others/shared_scaffold.dart
similarity index 100%
rename from packages/go_router/example/lib/shared_scaffold.dart
rename to packages/go_router/example/lib/others/shared_scaffold.dart
diff --git a/packages/go_router/example/lib/state_restoration.dart b/packages/go_router/example/lib/others/state_restoration.dart
similarity index 100%
rename from packages/go_router/example/lib/state_restoration.dart
rename to packages/go_router/example/lib/others/state_restoration.dart
diff --git a/packages/go_router/example/lib/transitions.dart b/packages/go_router/example/lib/others/transitions.dart
similarity index 100%
rename from packages/go_router/example/lib/transitions.dart
rename to packages/go_router/example/lib/others/transitions.dart
diff --git a/packages/go_router/example/lib/url_strategy.dart b/packages/go_router/example/lib/others/url_strategy.dart
similarity index 100%
rename from packages/go_router/example/lib/url_strategy.dart
rename to packages/go_router/example/lib/others/url_strategy.dart
diff --git a/packages/go_router/example/lib/user_input.dart b/packages/go_router/example/lib/others/user_input.dart
similarity index 99%
rename from packages/go_router/example/lib/user_input.dart
rename to packages/go_router/example/lib/others/user_input.dart
index 56cf9a9..93c972b 100644
--- a/packages/go_router/example/lib/user_input.dart
+++ b/packages/go_router/example/lib/others/user_input.dart
@@ -6,7 +6,7 @@
 import 'package:flutter/material.dart';
 import 'package:go_router/go_router.dart';
 
-import 'shared/data.dart';
+import '../shared/data.dart';
 
 void main() => runApp(App());
 
diff --git a/packages/go_router/example/lib/widgets_app.dart b/packages/go_router/example/lib/others/widgets_app.dart
similarity index 100%
rename from packages/go_router/example/lib/widgets_app.dart
rename to packages/go_router/example/lib/others/widgets_app.dart
diff --git a/packages/go_router/example/lib/path_and_query_parameters.dart b/packages/go_router/example/lib/path_and_query_parameters.dart
new file mode 100755
index 0000000..6a91c15
--- /dev/null
+++ b/packages/go_router/example/lib/path_and_query_parameters.dart
@@ -0,0 +1,132 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:go_router/go_router.dart';
+
+import 'shared/data.dart';
+
+// This scenario demonstrates how to use path parameters and query parameters.
+//
+// The route segments that start with ':' are treated as path parameters when
+// defining GoRoute[s]. The parameter values can be accessed through
+// GoRouterState.params.
+//
+// The query parameters are automatically stored in GoRouterState.queryParams.
+
+void main() => runApp(App());
+
+/// The main app.
+class App extends StatelessWidget {
+  /// Creates an [App].
+  App({Key? key}) : super(key: key);
+
+  /// The title of the app.
+  static const String title = 'GoRouter Example: Query Parameters';
+
+  // add the login info into the tree as app state that can change over time
+  @override
+  Widget build(BuildContext context) => MaterialApp.router(
+        routeInformationProvider: _router.routeInformationProvider,
+        routeInformationParser: _router.routeInformationParser,
+        routerDelegate: _router.routerDelegate,
+        title: title,
+        debugShowCheckedModeBanner: false,
+      );
+
+  late final GoRouter _router = GoRouter(
+    routes: <GoRoute>[
+      GoRoute(
+        path: '/',
+        builder: (BuildContext context, GoRouterState state) =>
+            HomeScreen(families: Families.data),
+        routes: <GoRoute>[
+          GoRoute(
+              path: 'family/:id',
+              builder: (BuildContext context, GoRouterState state) {
+                return FamilyScreen(
+                  family: Families.family(state.params['id']!),
+                  asc: state.queryParams['sort'] == 'asc',
+                );
+              }),
+        ],
+      ),
+    ],
+  );
+}
+
+/// The home screen that shows a list of families.
+class HomeScreen extends StatelessWidget {
+  /// Creates a [HomeScreen].
+  const HomeScreen({required this.families, Key? key}) : super(key: key);
+
+  /// The list of families.
+  final List<Family> families;
+
+  @override
+  Widget build(BuildContext context) {
+    return Scaffold(
+      appBar: AppBar(
+        title: const Text(App.title),
+      ),
+      body: ListView(
+        children: <Widget>[
+          for (final Family f in families)
+            ListTile(
+              title: Text(f.name),
+              onTap: () => context.go('/family/${f.id}'),
+            )
+        ],
+      ),
+    );
+  }
+}
+
+/// The screen that shows a list of persons in a family.
+class FamilyScreen extends StatelessWidget {
+  /// Creates a [FamilyScreen].
+  const FamilyScreen({required this.family, required this.asc, Key? key})
+      : super(key: key);
+
+  /// The family to display.
+  final Family family;
+
+  /// Whether to sort the name in ascending order.
+  final bool asc;
+
+  @override
+  Widget build(BuildContext context) {
+    final String curentPath = Uri.parse(GoRouter.of(context).location).path;
+    final Map<String, String> newQueries;
+    final List<String> names =
+        family.people.map<String>((Person p) => p.name).toList();
+    names.sort();
+    if (asc) {
+      newQueries = const <String, String>{'sort': 'desc'};
+    } else {
+      newQueries = const <String, String>{'sort': 'asc'};
+    }
+    final Uri iconLink = Uri(path: curentPath, queryParameters: newQueries);
+    return Scaffold(
+      appBar: AppBar(
+        title: Text(family.name),
+        actions: <Widget>[
+          IconButton(
+            onPressed: () => context.go(iconLink.toString()),
+            tooltip: 'sort ascending or descending',
+            icon: const Icon(Icons.sort),
+          )
+        ],
+      ),
+      body: ListView(
+        children: <Widget>[
+          for (final String name in asc ? names : names.reversed)
+            ListTile(
+              title: Text(name),
+            ),
+        ],
+      ),
+    );
+  }
+}
diff --git a/packages/go_router/example/lib/query_params.dart b/packages/go_router/example/lib/query_params.dart
deleted file mode 100644
index 656273e..0000000
--- a/packages/go_router/example/lib/query_params.dart
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright 2013 The Flutter Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-import 'package:flutter/material.dart';
-import 'package:go_router/go_router.dart';
-import 'package:provider/provider.dart';
-
-import 'shared/data.dart';
-
-void main() => runApp(App());
-
-/// The main app.
-class App extends StatelessWidget {
-  /// Creates an [App].
-  App({Key? key}) : super(key: key);
-
-  final LoginInfo _loginInfo = LoginInfo();
-
-  /// The title of the app.
-  static const String title = 'GoRouter Example: Query Parameters';
-
-  // add the login info into the tree as app state that can change over time
-  @override
-  Widget build(BuildContext context) => ChangeNotifierProvider<LoginInfo>.value(
-        value: _loginInfo,
-        child: MaterialApp.router(
-          routeInformationProvider: _router.routeInformationProvider,
-          routeInformationParser: _router.routeInformationParser,
-          routerDelegate: _router.routerDelegate,
-          title: title,
-          debugShowCheckedModeBanner: false,
-        ),
-      );
-
-  late final GoRouter _router = GoRouter(
-    routes: <GoRoute>[
-      GoRoute(
-        path: '/',
-        builder: (BuildContext context, GoRouterState state) =>
-            HomeScreen(families: Families.data),
-        routes: <GoRoute>[
-          GoRoute(
-            path: 'family/:fid',
-            builder: (BuildContext context, GoRouterState state) =>
-                FamilyScreen(
-              family: Families.family(state.params['fid']!),
-            ),
-            routes: <GoRoute>[
-              GoRoute(
-                path: 'person/:pid',
-                builder: (BuildContext context, GoRouterState state) {
-                  final Family family = Families.family(state.params['fid']!);
-                  final Person person = family.person(state.params['pid']!);
-                  return PersonScreen(family: family, person: person);
-                },
-              ),
-            ],
-          ),
-        ],
-      ),
-      GoRoute(
-        path: '/login',
-        builder: (BuildContext context, GoRouterState state) =>
-            const LoginScreen(),
-      ),
-    ],
-
-    // redirect to the login page if the user is not logged in
-    redirect: (GoRouterState state) {
-      // if the user is not logged in, they need to login
-      final bool loggedIn = _loginInfo.loggedIn;
-      final bool loggingIn = state.subloc == '/login';
-
-      // bundle the location the user is coming from into a query parameter
-      final String fromp = state.subloc == '/' ? '' : '?from=${state.subloc}';
-      if (!loggedIn) {
-        return loggingIn ? null : '/login$fromp';
-      }
-
-      // if the user is logged in, send them where they were going before (or
-      // home if they weren't going anywhere)
-      if (loggingIn) {
-        return state.queryParams['from'] ?? '/';
-      }
-
-      // no need to redirect at all
-      return null;
-    },
-
-    // changes on the listenable will cause the router to refresh it's route
-    refreshListenable: _loginInfo,
-  );
-}
-
-/// The home screen that shows a list of families.
-class HomeScreen extends StatelessWidget {
-  /// Creates a [HomeScreen].
-  const HomeScreen({required this.families, Key? key}) : super(key: key);
-
-  /// The list of families.
-  final List<Family> families;
-
-  @override
-  Widget build(BuildContext context) {
-    final LoginInfo info = context.read<LoginInfo>();
-
-    return Scaffold(
-      appBar: AppBar(
-        title: const Text(App.title),
-        actions: <Widget>[
-          IconButton(
-            onPressed: () {
-              info.logout();
-              context.go('/');
-            },
-            tooltip: 'Logout: ${info.userName}',
-            icon: const Icon(Icons.logout),
-          )
-        ],
-      ),
-      body: ListView(
-        children: <Widget>[
-          for (final Family f in families)
-            ListTile(
-              title: Text(f.name),
-              onTap: () => context.go('/family/${f.id}'),
-            )
-        ],
-      ),
-    );
-  }
-}
-
-/// The screen that shows a list of persons in a family.
-class FamilyScreen extends StatelessWidget {
-  /// Creates a [FamilyScreen].
-  const FamilyScreen({required this.family, Key? key}) : super(key: key);
-
-  /// The family to display.
-  final Family family;
-
-  @override
-  Widget build(BuildContext context) => Scaffold(
-        appBar: AppBar(title: Text(family.name)),
-        body: ListView(
-          children: <Widget>[
-            for (final Person p in family.people)
-              ListTile(
-                title: Text(p.name),
-                onTap: () => context.go('/family/${family.id}/person/${p.id}'),
-              ),
-          ],
-        ),
-      );
-}
-
-/// The person screen.
-class PersonScreen extends StatelessWidget {
-  /// Creates a [PersonScreen].
-  const PersonScreen({required this.family, required this.person, Key? key})
-      : super(key: key);
-
-  /// The family this person belong to.
-  final Family family;
-
-  /// The person to be displayed.
-  final Person person;
-
-  @override
-  Widget build(BuildContext context) => Scaffold(
-        appBar: AppBar(title: Text(person.name)),
-        body: Text('${person.name} ${family.name} is ${person.age} years old'),
-      );
-}
-
-/// The login screen.
-class LoginScreen extends StatelessWidget {
-  /// Creates a [LoginScreen].
-  const LoginScreen({Key? key}) : super(key: key);
-
-  @override
-  Widget build(BuildContext context) => Scaffold(
-        appBar: AppBar(title: const Text(App.title)),
-        body: Center(
-          child: Column(
-            mainAxisAlignment: MainAxisAlignment.center,
-            children: <Widget>[
-              ElevatedButton(
-                onPressed: () {
-                  // log a user in, letting all the listeners know
-                  context.read<LoginInfo>().login('test-user');
-                },
-                child: const Text('Login'),
-              ),
-            ],
-          ),
-        ),
-      );
-}
diff --git a/packages/go_router/example/lib/redirection.dart b/packages/go_router/example/lib/redirection.dart
index 0cbe6ed..5697fd6 100644
--- a/packages/go_router/example/lib/redirection.dart
+++ b/packages/go_router/example/lib/redirection.dart
@@ -8,6 +8,12 @@
 
 import 'shared/data.dart';
 
+// This scenario demonstrates how to use redirect to handle a sign-in flow.
+//
+// The GoRouter.redirect method is called before the app is navigate to a
+// new page. You can choose to redirect to a different page by returning a
+// non-null URL string.
+
 void main() => runApp(App());
 
 /// The main app.
@@ -38,26 +44,7 @@
       GoRoute(
         path: '/',
         builder: (BuildContext context, GoRouterState state) =>
-            HomeScreen(families: Families.data),
-        routes: <GoRoute>[
-          GoRoute(
-            path: 'family/:fid',
-            builder: (BuildContext context, GoRouterState state) =>
-                FamilyScreen(
-              family: Families.family(state.params['fid']!),
-            ),
-            routes: <GoRoute>[
-              GoRoute(
-                path: 'person/:pid',
-                builder: (BuildContext context, GoRouterState state) {
-                  final Family family = Families.family(state.params['fid']!);
-                  final Person person = family.person(state.params['pid']!);
-                  return PersonScreen(family: family, person: person);
-                },
-              ),
-            ],
-          ),
-        ],
+            const HomeScreen(),
       ),
       GoRoute(
         path: '/login',
@@ -109,7 +96,6 @@
 
                   // router will automatically redirect from /login to / using
                   // refreshListenable
-                  //context.go('/');
                 },
                 child: const Text('Login'),
               ),
@@ -119,13 +105,10 @@
       );
 }
 
-/// The home screen that shows a list of families.
+/// The home screen.
 class HomeScreen extends StatelessWidget {
   /// Creates a [HomeScreen].
-  const HomeScreen({required this.families, Key? key}) : super(key: key);
-
-  /// The list of families.
-  final List<Family> families;
+  const HomeScreen({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -142,57 +125,9 @@
           )
         ],
       ),
-      body: ListView(
-        children: <Widget>[
-          for (final Family f in families)
-            ListTile(
-              title: Text(f.name),
-              onTap: () => context.go('/family/${f.id}'),
-            )
-        ],
+      body: const Center(
+        child: Text('HomeScreen'),
       ),
     );
   }
 }
-
-/// The screen that shows a list of persons in a family.
-class FamilyScreen extends StatelessWidget {
-  /// Creates a [FamilyScreen].
-  const FamilyScreen({required this.family, Key? key}) : super(key: key);
-
-  /// The family to display.
-  final Family family;
-
-  @override
-  Widget build(BuildContext context) => Scaffold(
-        appBar: AppBar(title: Text(family.name)),
-        body: ListView(
-          children: <Widget>[
-            for (final Person p in family.people)
-              ListTile(
-                title: Text(p.name),
-                onTap: () => context.go('/family/${family.id}/person/${p.id}'),
-              ),
-          ],
-        ),
-      );
-}
-
-/// The person screen.
-class PersonScreen extends StatelessWidget {
-  /// Creates a [PersonScreen].
-  const PersonScreen({required this.family, required this.person, Key? key})
-      : super(key: key);
-
-  /// The family this person belong to.
-  final Family family;
-
-  /// The person to be displayed.
-  final Person person;
-
-  @override
-  Widget build(BuildContext context) => Scaffold(
-        appBar: AppBar(title: Text(person.name)),
-        body: Text('${person.name} ${family.name} is ${person.age} years old'),
-      );
-}
diff --git a/packages/go_router/example/lib/sub_routes.dart b/packages/go_router/example/lib/sub_routes.dart
index 68288dd..6e1632a 100644
--- a/packages/go_router/example/lib/sub_routes.dart
+++ b/packages/go_router/example/lib/sub_routes.dart
@@ -5,7 +5,17 @@
 import 'package:flutter/material.dart';
 import 'package:go_router/go_router.dart';
 
-import 'shared/data.dart';
+// This scenario demonstrates an app with multi-level routing.
+//
+// The GoRoute '/' builds a HomeScreen page and has a sub-route, 'family'. The
+// 'family' also has its own sub-route, person'.
+//
+// If a url matches a sub-route, the page built by the sub-route is placed on
+// top of the parent's page. In this example, a url '/family/person' will create
+// a stack of three pages, [PersonScreen, FamilyScreen, HomeScreen] where the
+// PersonScreen being the top-most page and shown on the screen. Since there are
+// two more pages under it, the back button is shown on the app bar and can pop
+// the page to show the pages underneath.
 
 void main() => runApp(App());
 
@@ -30,23 +40,17 @@
       GoRoute(
         path: '/',
         builder: (BuildContext context, GoRouterState state) =>
-            HomeScreen(families: Families.data),
+            const HomeScreen(),
         routes: <GoRoute>[
           GoRoute(
-            path: 'family/:fid',
+            path: 'family',
             builder: (BuildContext context, GoRouterState state) =>
-                FamilyScreen(
-              family: Families.family(state.params['fid']!),
-            ),
+                const FamilyScreen(),
             routes: <GoRoute>[
               GoRoute(
-                path: 'person/:pid',
-                builder: (BuildContext context, GoRouterState state) {
-                  final Family family = Families.family(state.params['fid']!);
-                  final Person person = family.person(state.params['pid']!);
-
-                  return PersonScreen(family: family, person: person);
-                },
+                path: 'person',
+                builder: (BuildContext context, GoRouterState state) =>
+                    const PersonScreen(),
               ),
             ],
           ),
@@ -59,22 +63,16 @@
 /// The home screen that shows a list of families.
 class HomeScreen extends StatelessWidget {
   /// Creates a [HomeScreen].
-  const HomeScreen({required this.families, Key? key}) : super(key: key);
-
-  /// The list of families.
-  final List<Family> families;
+  const HomeScreen({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) => Scaffold(
         appBar: AppBar(title: const Text(App.title)),
-        body: ListView(
-          children: <Widget>[
-            for (final Family f in families)
-              ListTile(
-                title: Text(f.name),
-                onTap: () => context.go('/family/${f.id}'),
-              )
-          ],
+        body: Center(
+          child: ElevatedButton(
+            onPressed: () => context.go('/family'),
+            child: const Text('Go to family screen'),
+          ),
         ),
       );
 }
@@ -82,22 +80,16 @@
 /// The screen that shows a list of persons in a family.
 class FamilyScreen extends StatelessWidget {
   /// Creates a [FamilyScreen].
-  const FamilyScreen({required this.family, Key? key}) : super(key: key);
-
-  /// The family to display.
-  final Family family;
+  const FamilyScreen({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) => Scaffold(
-        appBar: AppBar(title: Text(family.name)),
-        body: ListView(
-          children: <Widget>[
-            for (final Person p in family.people)
-              ListTile(
-                title: Text(p.name),
-                onTap: () => context.go('/family/${family.id}/person/${p.id}'),
-              ),
-          ],
+        appBar: AppBar(title: const Text('Family screen')),
+        body: Center(
+          child: ElevatedButton(
+            onPressed: () => context.go('/family/person'),
+            child: const Text('Go to person screen'),
+          ),
         ),
       );
 }
@@ -105,18 +97,12 @@
 /// The person screen.
 class PersonScreen extends StatelessWidget {
   /// Creates a [PersonScreen].
-  const PersonScreen({required this.family, required this.person, Key? key})
-      : super(key: key);
-
-  /// The family this person belong to.
-  final Family family;
-
-  /// The person to be displayed.
-  final Person person;
+  const PersonScreen({Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) => Scaffold(
-        appBar: AppBar(title: Text(person.name)),
-        body: Text('${person.name} ${family.name} is ${person.age} years old'),
-      );
+      appBar: AppBar(title: const Text('Person screen')),
+      body: const Center(
+        child: Text('This is the person screen'),
+      ));
 }
diff --git a/packages/go_router/example/test/main_test.dart b/packages/go_router/example/test/main_test.dart
new file mode 100644
index 0000000..f8e3ed0
--- /dev/null
+++ b/packages/go_router/example/test/main_test.dart
@@ -0,0 +1,21 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:go_router_examples/main.dart' as example;
+
+void main() {
+  testWidgets('example works', (WidgetTester tester) async {
+    await tester.pumpWidget(example.App());
+    expect(find.text('Go to page 2'), findsOneWidget);
+
+    await tester.tap(find.text('Go to page 2'));
+    await tester.pumpAndSettle();
+    expect(find.text('Go back to home page'), findsOneWidget);
+
+    await tester.tap(find.text('Go back to home page'));
+    await tester.pumpAndSettle();
+    expect(find.text('Go to page 2'), findsOneWidget);
+  });
+}
diff --git a/packages/go_router/example/test/path_and_query_params_test.dart b/packages/go_router/example/test/path_and_query_params_test.dart
new file mode 100644
index 0000000..02e2362
--- /dev/null
+++ b/packages/go_router/example/test/path_and_query_params_test.dart
@@ -0,0 +1,47 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:go_router_examples/path_and_query_parameters.dart' as example;
+
+void main() {
+  testWidgets('example works', (WidgetTester tester) async {
+    await tester.pumpWidget(example.App());
+    expect(find.text(example.App.title), findsOneWidget);
+
+    // Directly set the url through platform message.
+    Map<String, dynamic> testRouteInformation = <String, dynamic>{
+      'location': '/family/f1?sort=asc',
+    };
+    ByteData message = const JSONMethodCodec().encodeMethodCall(
+      MethodCall('pushRouteInformation', testRouteInformation),
+    );
+    await ServicesBinding.instance.defaultBinaryMessenger
+        .handlePlatformMessage('flutter/navigation', message, (_) {});
+
+    await tester.pumpAndSettle();
+    // 'Chris' should be higher than 'Tom'.
+    expect(
+        tester.getCenter(find.text('Chris')).dy <
+            tester.getCenter(find.text('Tom')).dy,
+        isTrue);
+
+    testRouteInformation = <String, dynamic>{
+      'location': '/family/f1?privacy=false',
+    };
+    message = const JSONMethodCodec().encodeMethodCall(
+      MethodCall('pushRouteInformation', testRouteInformation),
+    );
+    await ServicesBinding.instance.defaultBinaryMessenger
+        .handlePlatformMessage('flutter/navigation', message, (_) {});
+
+    await tester.pumpAndSettle();
+    // 'Chris' should be lower than 'Tom'.
+    expect(
+        tester.getCenter(find.text('Chris')).dy >
+            tester.getCenter(find.text('Tom')).dy,
+        isTrue);
+  });
+}
diff --git a/packages/go_router/example/test/reidrection_test.dart b/packages/go_router/example/test/reidrection_test.dart
new file mode 100644
index 0000000..21a366e
--- /dev/null
+++ b/packages/go_router/example/test/reidrection_test.dart
@@ -0,0 +1,52 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:go_router_examples/redirection.dart' as example;
+
+void main() {
+  testWidgets('example works', (WidgetTester tester) async {
+    await tester.pumpWidget(example.App());
+    expect(find.text('Login'), findsOneWidget);
+
+    // Directly set the url to the home page.
+    Map<String, dynamic> testRouteInformation = <String, dynamic>{
+      'location': '/',
+    };
+    ByteData message = const JSONMethodCodec().encodeMethodCall(
+      MethodCall('pushRouteInformation', testRouteInformation),
+    );
+    await ServicesBinding.instance.defaultBinaryMessenger
+        .handlePlatformMessage('flutter/navigation', message, (_) {});
+
+    await tester.pumpAndSettle();
+    // Still show login page due to redirection
+    expect(find.text('Login'), findsOneWidget);
+
+    await tester.tap(find.text('Login'));
+    await tester.pumpAndSettle();
+    expect(find.text('HomeScreen'), findsOneWidget);
+
+    testRouteInformation = <String, dynamic>{
+      'location': '/login',
+    };
+    message = const JSONMethodCodec().encodeMethodCall(
+      MethodCall('pushRouteInformation', testRouteInformation),
+    );
+    await ServicesBinding.instance.defaultBinaryMessenger
+        .handlePlatformMessage('flutter/navigation', message, (_) {});
+
+    await tester.pumpAndSettle();
+    // Got redirected back to home page.
+    expect(find.text('HomeScreen'), findsOneWidget);
+
+    // Tap logout.
+    await tester.tap(find.byType(IconButton));
+    await tester.pumpAndSettle();
+
+    expect(find.text('Login'), findsOneWidget);
+  });
+}
diff --git a/packages/go_router/example/test/sub_routes_test.dart b/packages/go_router/example/test/sub_routes_test.dart
new file mode 100644
index 0000000..9c5dc70
--- /dev/null
+++ b/packages/go_router/example/test/sub_routes_test.dart
@@ -0,0 +1,33 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:go_router_examples/sub_routes.dart' as example;
+
+void main() {
+  testWidgets('example works', (WidgetTester tester) async {
+    await tester.pumpWidget(example.App());
+    expect(find.text('Go to family screen'), findsOneWidget);
+
+    await tester.tap(find.text('Go to family screen'));
+    await tester.pumpAndSettle();
+    expect(find.text('Go to person screen'), findsOneWidget);
+
+    await tester.tap(find.text('Go to person screen'));
+    await tester.pumpAndSettle();
+    expect(find.text('This is the person screen'), findsOneWidget);
+    expect(find.byType(BackButton), findsOneWidget);
+
+    await tester.tap(find.byType(BackButton));
+    await tester.pumpAndSettle();
+    expect(find.text('Go to person screen'), findsOneWidget);
+    expect(find.byType(BackButton), findsOneWidget);
+
+    await tester.tap(find.byType(BackButton));
+    await tester.pumpAndSettle();
+    expect(find.text('Go to family screen'), findsOneWidget);
+    expect(find.byType(BackButton), findsNothing);
+  });
+}
diff --git a/packages/go_router/lib/src/route.dart b/packages/go_router/lib/src/route.dart
index d753313..d7790f5 100644
--- a/packages/go_router/lib/src/route.dart
+++ b/packages/go_router/lib/src/route.dart
@@ -61,11 +61,44 @@
   /// Optional name of the route.
   ///
   /// If used, a unique string name must be provided and it can not be empty.
+  ///
+  /// This is used in [GoRouter.namedLocation] and its related API. This
+  /// property can be used to navigate to this route without knowing exact the
+  /// URI of it.
+  ///
+  /// {@tool snippet}
+  /// Typical usage is as follows:
+  ///
+  /// ```dart
+  /// GoRoute(
+  ///   name: 'home',
+  ///   path: '/',
+  ///   builder: (BuildContext context, GoRouterState state) =>
+  ///       HomeScreen(),
+  ///   routes: <GoRoute>[
+  ///     GoRoute(
+  ///       name: 'family',
+  ///       path: 'family/:fid',
+  ///       builder: (BuildContext context, GoRouterState state) =>
+  ///           FamilyScreen(),
+  ///     ),
+  ///   ],
+  /// );
+  ///
+  /// context.go(
+  ///   context.namedLocation('family'),
+  ///   params: <String, String>{'fid': 123},
+  ///   queryParams: <String, String>{'qid': 'quid'},
+  /// );
+  /// ```
+  ///
+  /// See the [named routes example](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/named_routes.dart)
+  /// for a complete runnable app.
   final String? name;
 
   /// The path of this go route.
   ///
-  /// For example in:
+  /// For example:
   /// ```
   /// GoRoute(
   ///   path: '/',
@@ -75,6 +108,17 @@
   ///   ),
   /// ),
   /// ```
+  ///
+  /// The path also support path parameters. For a path: `/family/:fid`, it
+  /// matches all URIs start with `/family/...`, e.g. `/family/123`,
+  /// `/family/456` and etc. The parameter values are stored in [GoRouterState]
+  /// that are passed into [pageBuilder] and [builder].
+  ///
+  /// The query parameter are also capture during the route parsing and stored
+  /// in [GoRouterState].
+  ///
+  /// See [Query parameters and path parameters](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/sub_routes.dart)
+  /// to learn more about parameters.
   final String path;
 
   /// A page builder for this route.
@@ -186,12 +230,15 @@
   /// ```
   /// In the above example, if /family route is matched, it will be used.
   /// else /:username route will be used.
+  ///
+  /// See [Sub-routes](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/sub_routes.dart)
+  /// for a complete runnable example.
   final List<GoRoute> routes;
 
   /// An optional redirect function for this route.
   ///
   /// In the case that you like to make a redirection decision for a specific
-  /// route (or sub-route), you can do so by passing a redirect function to
+  /// route (or sub-route), consider doing so by passing a redirect function to
   /// the GoRoute constructor.
   ///
   /// For example:
@@ -209,6 +256,11 @@
   ///   ],
   /// );
   /// ```
+  ///
+  /// Redirect can also be used for conditionally preventing users from visiting
+  /// routes, also known as route guards. One canonical example is user
+  /// authentication. See [Redirection](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/redirection.dart)
+  /// for a complete runnable example.
   final GoRouterRedirect redirect;
 
   /// Match this route against a location.
diff --git a/packages/go_router/lib/src/router.dart b/packages/go_router/lib/src/router.dart
index c49c356..644480f 100644
--- a/packages/go_router/lib/src/router.dart
+++ b/packages/go_router/lib/src/router.dart
@@ -16,11 +16,30 @@
 
 /// The top-level go router class.
 ///
-/// Create one of these to initialize your app's routing policy.
-// ignore: prefer_mixin
+/// This is the main entry point for defining app's routing policy.
+///
+/// The `routes` defines the routing table. It must not be empty and must
+/// contain an [GoRouter] to match `/`.
+///
+/// See [Routes](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/main.dart)
+/// for an example of defining a simple routing table.
+///
+/// See [Sub-routes](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/sub_routes.dart)
+/// for an example of defining a multi-level routing table.
+///
+/// The `redirect` does top-level redirection before the URIs are parsed by
+/// the `routes`. Consider using [GoRoute.redirect] for individual route
+/// redirection.
+///
+/// See also:
+///  * [GoRoute], which provides APIs to define the routing table.
+///  * [examples](https://github.com/flutter/packages/tree/main/packages/go_router/example),
+///    which contains examples for different routing scenarios.
 class GoRouter extends ChangeNotifier with NavigatorObserver {
   /// Default constructor to configure a GoRouter with a routes builder
   /// and an error page builder.
+  ///
+  /// The `routes` must not be null and must contain an [GoRouter] to match `/`.
   GoRouter({
     required List<GoRoute> routes,
     // TODO(johnpryan): Change to a route, improve error API
diff --git a/packages/go_router/lib/src/state.dart b/packages/go_router/lib/src/state.dart
index e616dc4..5e9c4ab 100644
--- a/packages/go_router/lib/src/state.dart
+++ b/packages/go_router/lib/src/state.dart
@@ -7,6 +7,8 @@
 import 'configuration.dart';
 
 /// The route state during routing.
+///
+/// The state contains parsed artifacts of the current URI.
 class GoRouterState {
   /// Default constructor for creating route state during routing.
   GoRouterState(
diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml
index 0b2d6f0..722962e 100644
--- a/packages/go_router/pubspec.yaml
+++ b/packages/go_router/pubspec.yaml
@@ -1,7 +1,7 @@
 name: go_router
 description: A declarative router for Flutter based on Navigation 2 supporting
   deep linking, data-driven routes and more
-version: 4.2.3
+version: 4.2.4
 repository: https://github.com/flutter/packages/tree/main/packages/go_router
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22