Skip to content

Commit 58c02e0

Browse files
authored
[go_router_builder]: Add support for relative routes (#9749)
Adds support for TypedRelativeGoRoute. Builds on #9732 Continuation of #8476 by @ThangVuNguyenViet Compared to #8476, the approach taken in this PR avoids breaking changes and creates a separate RouteData subclass instead of extending GoRouteData. The approach increases flexibility and avoids extending behavior that might not make sense for a relative route. Fully resolves flutter/flutter#108177. ## Pre-Review Checklist **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 6a12043 commit 58c02e0

21 files changed

+961
-78
lines changed

packages/go_router_builder/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 3.3.0
2+
3+
- Adds support for `TypedRelativeGoRoute`.
4+
15
## 3.2.1
26

37
- Changes generated whitespace for better compatibility with new Dart formatter.

packages/go_router_builder/README.md

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ To use `go_router_builder`, you need to have the following dependencies in
88
```yaml
99
dependencies:
1010
# ...along with your other dependencies
11-
go_router: ^16.0.0
11+
go_router: ^16.2.0
1212

1313
dev_dependencies:
1414
# ...along with your other dev-dependencies
1515
build_runner: ^2.6.0
16-
go_router_builder: ^3.1.0
16+
go_router_builder: ^3.3.0
1717
```
1818
1919
### Source code
@@ -455,6 +455,32 @@ class MyGoRouteData extends GoRouteData with _$MyGoRouteData {
455455

456456
An example is available [here](https://github.com/flutter/packages/blob/main/packages/go_router_builder/example/lib/shell_route_with_keys_example.dart).
457457

458+
## Relative routes
459+
460+
Relative routes allow reusing the same `RouteData` in different parts of the route tree.
461+
Define a relative route by extending `RelativeGoRouteData`.
462+
463+
<?code-excerpt "example/lib/readme_excerpts.dart (relativeRoute)"?>
464+
```dart
465+
@TypedRelativeGoRoute<DetailsRoute>(path: 'details')
466+
class DetailsRoute extends RelativeGoRouteData with _$DetailsRoute {
467+
const DetailsRoute();
468+
469+
@override
470+
Widget build(BuildContext context, GoRouterState state) =>
471+
const DetailsScreen();
472+
}
473+
```
474+
475+
Navigate using the `goRelative` or `pushRelative` methods provided by the code generator:
476+
477+
<?code-excerpt "example/lib/readme_excerpts.dart (goRelative)"?>
478+
```dart
479+
void onTapRelative() => const DetailsRoute().goRelative(context);
480+
```
481+
482+
Relative routing methods are not idempotent and will cause an error when the relative location does not match a route.
483+
458484
## Run tests
459485

460486
To run unit tests, run command `dart tool/run_tests.dart` from `packages/go_router_builder/`.
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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+
// ignore_for_file: public_member_api_docs, unreachable_from_main
6+
7+
import 'package:flutter/material.dart';
8+
import 'package:go_router/go_router.dart';
9+
10+
part 'go_relative.g.dart';
11+
12+
void main() => runApp(const MyApp());
13+
14+
/// The main app.
15+
class MyApp extends StatelessWidget {
16+
/// Constructs a [MyApp]
17+
const MyApp({super.key});
18+
19+
@override
20+
Widget build(BuildContext context) {
21+
return MaterialApp.router(routerConfig: _router);
22+
}
23+
}
24+
25+
/// The route configuration.
26+
final GoRouter _router = GoRouter(routes: $appRoutes);
27+
const TypedRelativeGoRoute<DetailsRoute> detailRoute =
28+
TypedRelativeGoRoute<DetailsRoute>(
29+
path: 'details/:detailId',
30+
routes: <TypedRoute<RouteData>>[
31+
TypedRelativeGoRoute<SettingsRoute>(path: 'settings/:settingId'),
32+
],
33+
);
34+
35+
@TypedGoRoute<HomeRoute>(
36+
path: '/',
37+
routes: <TypedRoute<RouteData>>[
38+
TypedGoRoute<DashboardRoute>(
39+
path: '/dashboard',
40+
routes: <TypedRoute<RouteData>>[detailRoute],
41+
),
42+
detailRoute,
43+
],
44+
)
45+
class HomeRoute extends GoRouteData with _$HomeRoute {
46+
@override
47+
Widget build(BuildContext context, GoRouterState state) {
48+
return const HomeScreen();
49+
}
50+
}
51+
52+
class DashboardRoute extends GoRouteData with _$DashboardRoute {
53+
@override
54+
Widget build(BuildContext context, GoRouterState state) {
55+
return const DashboardScreen();
56+
}
57+
}
58+
59+
class DetailsRoute extends RelativeGoRouteData with _$DetailsRoute {
60+
const DetailsRoute({required this.detailId});
61+
final String detailId;
62+
63+
@override
64+
Widget build(BuildContext context, GoRouterState state) {
65+
return DetailsScreen(id: detailId);
66+
}
67+
}
68+
69+
class SettingsRoute extends RelativeGoRouteData with _$SettingsRoute {
70+
const SettingsRoute({required this.settingId});
71+
final String settingId;
72+
73+
@override
74+
Widget build(BuildContext context, GoRouterState state) {
75+
return SettingsScreen(id: settingId);
76+
}
77+
}
78+
79+
/// The home screen
80+
class HomeScreen extends StatelessWidget {
81+
/// Constructs a [HomeScreen]
82+
const HomeScreen({super.key});
83+
84+
@override
85+
Widget build(BuildContext context) {
86+
return Scaffold(
87+
appBar: AppBar(title: const Text('Home Screen')),
88+
body: Column(
89+
mainAxisAlignment: MainAxisAlignment.center,
90+
children: <Widget>[
91+
ElevatedButton(
92+
onPressed: () {
93+
const DetailsRoute(detailId: 'DetailsId').goRelative(context);
94+
},
95+
child: const Text('Go to the Details screen'),
96+
),
97+
ElevatedButton(
98+
onPressed: () {
99+
DashboardRoute().go(context);
100+
},
101+
child: const Text('Go to the Dashboard screen'),
102+
),
103+
],
104+
),
105+
);
106+
}
107+
}
108+
109+
/// The home screen
110+
class DashboardScreen extends StatelessWidget {
111+
/// Constructs a [DashboardScreen]
112+
const DashboardScreen({super.key});
113+
114+
@override
115+
Widget build(BuildContext context) {
116+
return Scaffold(
117+
appBar: AppBar(title: const Text('Dashboard Screen')),
118+
body: Column(
119+
children: <Widget>[
120+
ElevatedButton(
121+
onPressed: () {
122+
const DetailsRoute(detailId: 'DetailsId').goRelative(context);
123+
},
124+
child: const Text('Go to the Details screen'),
125+
),
126+
ElevatedButton(
127+
onPressed: () => context.pop(),
128+
child: const Text('Go back'),
129+
),
130+
],
131+
),
132+
);
133+
}
134+
}
135+
136+
/// The details screen
137+
class DetailsScreen extends StatelessWidget {
138+
/// Constructs a [DetailsScreen]
139+
const DetailsScreen({super.key, required this.id});
140+
141+
final String id;
142+
143+
@override
144+
Widget build(BuildContext context) {
145+
return Scaffold(
146+
appBar: AppBar(title: Text('Details Screen $id')),
147+
body: Center(
148+
child: Column(
149+
children: <Widget>[
150+
ElevatedButton(
151+
onPressed: () => context.pop(),
152+
child: const Text('Go back'),
153+
),
154+
ElevatedButton(
155+
onPressed:
156+
() => const SettingsRoute(
157+
settingId: 'SettingsId',
158+
).goRelative(context),
159+
child: const Text('Go to the Settings screen'),
160+
),
161+
],
162+
),
163+
),
164+
);
165+
}
166+
}
167+
168+
/// The details screen
169+
class SettingsScreen extends StatelessWidget {
170+
/// Constructs a [SettingsScreen]
171+
const SettingsScreen({super.key, required this.id});
172+
173+
final String id;
174+
175+
@override
176+
Widget build(BuildContext context) {
177+
return Scaffold(
178+
appBar: AppBar(title: Text('Settings Screen $id')),
179+
body: Center(
180+
child: TextButton(
181+
onPressed: () => context.pop(),
182+
child: const Text('Go back'),
183+
),
184+
),
185+
);
186+
}
187+
}

packages/go_router_builder/example/lib/go_relative.g.dart

Lines changed: 148 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)