Skip to content

Commit 6660212

Browse files
authored
[go_router] Refactored RouteMatchList and imperative APIs (#5497)
This pr refactor RouteMatchList to be a tree structure. Added a common base class RouteMatchBase. It is extended by both RouteMatch and ShellRouteMatch. The RouteMatch is for GoRoute, and is always a leaf node The ShellRouteMatch is for ShellRouteBase, and is always and intermediate node with a list of child RouteMatchBase[s]. This pr also redo how push is processed. Will add a doc explain this shortly. This is a breaking change, will write a migration guide soon. fixes flutter/flutter#134524 fixes flutter/flutter#130406 fixes flutter/flutter#126365 fixes flutter/flutter#125752 fixes flutter/flutter#120791 fixes flutter/flutter#120665 fixes flutter/flutter#113001 fixes flutter/flutter#110512
1 parent 19384b8 commit 6660212

23 files changed

+1688
-892
lines changed

packages/go_router/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 13.0.0
2+
3+
- Refactors `RouteMatchList` and imperative APIs.
4+
- **BREAKING CHANGE**:
5+
- RouteMatchList structure changed.
6+
- Matching logic updated.
7+
18
## 12.1.3
29

310
* Fixes a typo in `navigation.md`.

packages/go_router/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ See the API documentation for details on the following topics:
3737
- [Error handling](https://pub.dev/documentation/go_router/latest/topics/Error%20handling-topic.html)
3838

3939
## Migration Guides
40+
- [Migrating to 13.0.0](https://flutter.dev/go/go-router-v13-breaking-changes).
4041
- [Migrating to 12.0.0](https://flutter.dev/go/go-router-v12-breaking-changes).
4142
- [Migrating to 11.0.0](https://flutter.dev/go/go-router-v11-breaking-changes).
4243
- [Migrating to 10.0.0](https://flutter.dev/go/go-router-v10-breaking-changes).
@@ -67,4 +68,3 @@ The project follows the same priority system as flutter framework.
6768
[P3](https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-asc+label%3Ateam-go_router+label%3AP3+)
6869

6970
[Package PRs](https://github.com/flutter/packages/pulls?q=is%3Apr+is%3Aopen+label%3A%22p%3A+go_router%22%2C%22p%3A+go_router_builder%22)
70-

packages/go_router/doc/navigation.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,27 @@ Navigator.of(context).push(
6868
);
6969
```
7070

71+
The behavior may change depends on the shell route in current screen and the new screen.
72+
73+
If pushing a new screen without any shell route onto the current screen with shell route, the new
74+
screen is placed entirely on top of the current screen.
75+
76+
![An animation shows a new screen push on top of current screen](https://flutter.github.io/assets-for-api-docs/assets/go_router/push_regular_route.gif)
77+
78+
If pushing a new screen with the same shell route as the current screen, the new
79+
screen is placed inside of the shell.
80+
81+
![An animation shows pushing a new screen with the same shell as current screen](https://flutter.github.io/assets-for-api-docs/assets/go_router/push_same_shell.gif)
82+
83+
If pushing a new screen with the different shell route as the current screen, the new
84+
screen along with the shell is placed entirely on top of the current screen.
85+
86+
![An animation shows pushing a new screen with the different shell as current screen](https://flutter.github.io/assets-for-api-docs/assets/go_router/push_different_shell.gif)
87+
88+
To try out the behavior yourself, see
89+
[push_with_shell_route.dart](https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/extra_codec.dart).
90+
91+
7192
## Returning values
7293
Waiting for a value to be returned:
7394

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:go_router/go_router.dart';
7+
8+
// This scenario demonstrates the behavior when pushing ShellRoute in various
9+
// scenario.
10+
//
11+
// This example have three routes, /shell1, /shell2, and /regular-route. The
12+
// /shell1 and /shell2 are nested in different ShellRoutes. The /regular-route
13+
// is a simple GoRoute.
14+
15+
void main() {
16+
runApp(PushWithShellRouteExampleApp());
17+
}
18+
19+
/// An example demonstrating how to use [ShellRoute]
20+
class PushWithShellRouteExampleApp extends StatelessWidget {
21+
/// Creates a [PushWithShellRouteExampleApp]
22+
PushWithShellRouteExampleApp({super.key});
23+
24+
final GoRouter _router = GoRouter(
25+
initialLocation: '/home',
26+
debugLogDiagnostics: true,
27+
routes: <RouteBase>[
28+
ShellRoute(
29+
builder: (BuildContext context, GoRouterState state, Widget child) {
30+
return ScaffoldForShell1(child: child);
31+
},
32+
routes: <RouteBase>[
33+
GoRoute(
34+
path: '/home',
35+
builder: (BuildContext context, GoRouterState state) {
36+
return const Home();
37+
},
38+
),
39+
GoRoute(
40+
path: '/shell1',
41+
pageBuilder: (_, __) => const NoTransitionPage<void>(
42+
child: Center(
43+
child: Text('shell1 body'),
44+
),
45+
),
46+
),
47+
],
48+
),
49+
ShellRoute(
50+
builder: (BuildContext context, GoRouterState state, Widget child) {
51+
return ScaffoldForShell2(child: child);
52+
},
53+
routes: <RouteBase>[
54+
GoRoute(
55+
path: '/shell2',
56+
builder: (BuildContext context, GoRouterState state) {
57+
return const Center(child: Text('shell2 body'));
58+
},
59+
),
60+
],
61+
),
62+
GoRoute(
63+
path: '/regular-route',
64+
builder: (BuildContext context, GoRouterState state) {
65+
return const Scaffold(
66+
body: Center(child: Text('regular route')),
67+
);
68+
},
69+
),
70+
],
71+
);
72+
73+
@override
74+
Widget build(BuildContext context) {
75+
return MaterialApp.router(
76+
title: 'Flutter Demo',
77+
theme: ThemeData(
78+
primarySwatch: Colors.blue,
79+
),
80+
routerConfig: _router,
81+
);
82+
}
83+
}
84+
85+
/// Builds the "shell" for /shell1
86+
class ScaffoldForShell1 extends StatelessWidget {
87+
/// Constructs an [ScaffoldForShell1].
88+
const ScaffoldForShell1({
89+
required this.child,
90+
super.key,
91+
});
92+
93+
/// The widget to display in the body of the Scaffold.
94+
/// In this sample, it is a Navigator.
95+
final Widget child;
96+
97+
@override
98+
Widget build(BuildContext context) {
99+
return Scaffold(
100+
appBar: AppBar(title: const Text('shell1')),
101+
body: child,
102+
);
103+
}
104+
}
105+
106+
/// Builds the "shell" for /shell1
107+
class ScaffoldForShell2 extends StatelessWidget {
108+
/// Constructs an [ScaffoldForShell1].
109+
const ScaffoldForShell2({
110+
required this.child,
111+
super.key,
112+
});
113+
114+
/// The widget to display in the body of the Scaffold.
115+
/// In this sample, it is a Navigator.
116+
final Widget child;
117+
118+
@override
119+
Widget build(BuildContext context) {
120+
return Scaffold(
121+
appBar: AppBar(title: const Text('shell2')),
122+
body: child,
123+
);
124+
}
125+
}
126+
127+
/// The screen for /home
128+
class Home extends StatelessWidget {
129+
/// Constructs a [Home] widget.
130+
const Home({super.key});
131+
132+
@override
133+
Widget build(BuildContext context) {
134+
return Center(
135+
child: Column(
136+
mainAxisSize: MainAxisSize.min,
137+
children: <Widget>[
138+
TextButton(
139+
onPressed: () {
140+
GoRouter.of(context).push('/shell1');
141+
},
142+
child: const Text('push the same shell route /shell1'),
143+
),
144+
TextButton(
145+
onPressed: () {
146+
GoRouter.of(context).push('/shell2');
147+
},
148+
child: const Text('push the different shell route /shell2'),
149+
),
150+
TextButton(
151+
onPressed: () {
152+
GoRouter.of(context).push('/regular-route');
153+
},
154+
child: const Text('push the regular route /regular-route'),
155+
),
156+
],
157+
),
158+
);
159+
}
160+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:go_router/go_router.dart';
7+
import 'package:go_router_examples/push_with_shell_route.dart' as example;
8+
9+
void main() {
10+
testWidgets('example works', (WidgetTester tester) async {
11+
await tester.pumpWidget(example.PushWithShellRouteExampleApp());
12+
expect(find.text('shell1'), findsOneWidget);
13+
14+
await tester.tap(find.text('push the same shell route /shell1'));
15+
await tester.pumpAndSettle();
16+
expect(find.text('shell1'), findsOneWidget);
17+
expect(find.text('shell1 body'), findsOneWidget);
18+
19+
find.text('shell1 body').evaluate().first.pop();
20+
await tester.pumpAndSettle();
21+
expect(find.text('shell1'), findsOneWidget);
22+
expect(find.text('shell1 body'), findsNothing);
23+
24+
await tester.tap(find.text('push the different shell route /shell2'));
25+
await tester.pumpAndSettle();
26+
expect(find.text('shell1'), findsNothing);
27+
expect(find.text('shell2'), findsOneWidget);
28+
expect(find.text('shell2 body'), findsOneWidget);
29+
30+
find.text('shell2 body').evaluate().first.pop();
31+
await tester.pumpAndSettle();
32+
expect(find.text('shell1'), findsOneWidget);
33+
expect(find.text('shell2'), findsNothing);
34+
35+
await tester.tap(find.text('push the regular route /regular-route'));
36+
await tester.pumpAndSettle();
37+
expect(find.text('shell1'), findsNothing);
38+
expect(find.text('regular route'), findsOneWidget);
39+
40+
find.text('regular route').evaluate().first.pop();
41+
await tester.pumpAndSettle();
42+
expect(find.text('shell1'), findsOneWidget);
43+
expect(find.text('regular route'), findsNothing);
44+
});
45+
}

0 commit comments

Comments
 (0)