Skip to content

Commit f778d2e

Browse files
committed
Add RelativeGoRouteData and TypedRelativeGoRoute
1 parent b535930 commit f778d2e

File tree

4 files changed

+403
-34
lines changed

4 files changed

+403
-34
lines changed

packages/go_router/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 16.0.1
2+
3+
- Adds `RelativeGoRouteData` and `TypedRelativeGoRoute`.
4+
15
## 16.0.0
26

37
- **BREAKING CHANGE**

packages/go_router/lib/src/route_data.dart

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ abstract class GoRouteData extends RouteData {
134134
'GoRouteState to GoRouteData expando',
135135
);
136136

137-
/// The location of this route.
137+
/// The location of this route, e.g. /family/f2/person/p1
138138
String get location => throw _shouldBeGeneratedError;
139139

140140
/// Navigate to the route.
@@ -159,6 +159,151 @@ abstract class GoRouteData extends RouteData {
159159
);
160160
}
161161

162+
/// A class to represent a relative [GoRoute] in
163+
/// [Type-safe routing](https://pub.dev/documentation/go_router/latest/topics/Type-safe%20routes-topic.html).
164+
///
165+
/// Subclasses must override one of [build], [buildPage], or
166+
/// [redirect].
167+
/// {@category Type-safe routes}
168+
abstract class RelativeGoRouteData extends RouteData {
169+
/// Allows subclasses to have `const` constructors.
170+
///
171+
/// [RelativeGoRouteData] is abstract and cannot be instantiated directly.
172+
const RelativeGoRouteData();
173+
174+
/// Creates the [Widget] for `this` route.
175+
///
176+
/// Subclasses must override one of [build], [buildPage], or
177+
/// [redirect].
178+
///
179+
/// Corresponds to [GoRoute.builder].
180+
Widget build(BuildContext context, GoRouterState state) =>
181+
throw UnimplementedError(
182+
'One of `build` or `buildPage` must be implemented.',
183+
);
184+
185+
/// A page builder for this route.
186+
///
187+
/// Subclasses can override this function to provide a custom [Page].
188+
///
189+
/// Subclasses must override one of [build], [buildPage] or
190+
/// [redirect].
191+
///
192+
/// Corresponds to [GoRoute.pageBuilder].
193+
///
194+
/// By default, returns a [Page] instance that is ignored, causing a default
195+
/// [Page] implementation to be used with the results of [build].
196+
Page<void> buildPage(BuildContext context, GoRouterState state) =>
197+
const NoOpPage();
198+
199+
/// An optional redirect function for this route.
200+
///
201+
/// Subclasses must override one of [build], [buildPage], or
202+
/// [redirect].
203+
///
204+
/// Corresponds to [GoRoute.redirect].
205+
FutureOr<String?> redirect(BuildContext context, GoRouterState state) => null;
206+
207+
/// Called when this route is removed from GoRouter's route history.
208+
///
209+
/// Corresponds to [GoRoute.onExit].
210+
FutureOr<bool> onExit(BuildContext context, GoRouterState state) => true;
211+
212+
/// A helper function used by generated code.
213+
///
214+
/// Should not be used directly.
215+
static String $location(String path, {Map<String, dynamic>? queryParams}) =>
216+
Uri.parse(path)
217+
.replace(
218+
queryParameters:
219+
// Avoid `?` in generated location if `queryParams` is empty
220+
queryParams?.isNotEmpty ?? false ? queryParams : null,
221+
)
222+
.toString();
223+
224+
/// A helper function used by generated code.
225+
///
226+
/// Should not be used directly.
227+
static GoRoute $route<T extends RelativeGoRouteData>({
228+
required String path,
229+
bool caseSensitive = true,
230+
required T Function(GoRouterState) factory,
231+
GlobalKey<NavigatorState>? parentNavigatorKey,
232+
List<RouteBase> routes = const <RouteBase>[],
233+
}) {
234+
T factoryImpl(GoRouterState state) {
235+
final Object? extra = state.extra;
236+
237+
// If the "extra" value is of type `T` then we know it's the source
238+
// instance of `RelativeGoRouteData`, so it doesn't need to be recreated.
239+
if (extra is T) {
240+
return extra;
241+
}
242+
243+
return (_stateObjectExpando[state] ??= factory(state)) as T;
244+
}
245+
246+
Widget builder(BuildContext context, GoRouterState state) =>
247+
factoryImpl(state).build(context, state);
248+
249+
Page<void> pageBuilder(BuildContext context, GoRouterState state) =>
250+
factoryImpl(state).buildPage(context, state);
251+
252+
FutureOr<String?> redirect(BuildContext context, GoRouterState state) =>
253+
factoryImpl(state).redirect(context, state);
254+
255+
FutureOr<bool> onExit(BuildContext context, GoRouterState state) =>
256+
factoryImpl(state).onExit(context, state);
257+
258+
return GoRoute(
259+
path: path,
260+
caseSensitive: caseSensitive,
261+
builder: builder,
262+
pageBuilder: pageBuilder,
263+
redirect: redirect,
264+
routes: routes,
265+
parentNavigatorKey: parentNavigatorKey,
266+
onExit: onExit,
267+
);
268+
}
269+
270+
/// Used to cache [RelativeGoRouteData] that corresponds to a given [GoRouterState]
271+
/// to minimize the number of times it has to be deserialized.
272+
static final Expando<RelativeGoRouteData> _stateObjectExpando =
273+
Expando<RelativeGoRouteData>(
274+
'GoRouteState to RelativeGoRouteData expando',
275+
);
276+
277+
/// The location of this route, e.g. person/p1
278+
String get location => throw _shouldBeGeneratedError;
279+
280+
/// The relative location of this route, e.g. ./person/p1
281+
String get relativeLocation => throw _shouldBeGeneratedError;
282+
283+
/// Navigate to the route.
284+
void goRelative(BuildContext context) => throw _shouldBeGeneratedError;
285+
286+
/// Push the route onto the page stack.
287+
Future<T?> pushRelative<T>(BuildContext context) =>
288+
throw _shouldBeGeneratedError;
289+
290+
/// Replaces the top-most page of the page stack with the route.
291+
void pushReplacementRelative(BuildContext context) =>
292+
throw _shouldBeGeneratedError;
293+
294+
/// Replaces the top-most page of the page stack with the route but treats
295+
/// it as the same page.
296+
///
297+
/// The page key will be reused. This will preserve the state and not run any
298+
/// page animation.
299+
///
300+
void replaceRelative(BuildContext context) => throw _shouldBeGeneratedError;
301+
302+
static UnimplementedError get _shouldBeGeneratedError => UnimplementedError(
303+
'Should be generated using [Type-safe routing](https://pub.dev/documentation/go_router/latest/topics/Type-safe%20routes-topic.html).',
304+
);
305+
}
306+
162307
/// A class to represent a [ShellRoute] in
163308
/// [Type-safe routing](https://pub.dev/documentation/go_router/latest/topics/Type-safe%20routes-topic.html).
164309
abstract class ShellRouteData extends RouteData {
@@ -430,6 +575,41 @@ class TypedGoRoute<T extends GoRouteData> extends TypedRoute<T> {
430575
final bool caseSensitive;
431576
}
432577

578+
/// A superclass for each typed relative go route descendant
579+
@Target(<TargetKind>{TargetKind.library, TargetKind.classType})
580+
class TypedRelativeGoRoute<T extends RelativeGoRouteData>
581+
extends TypedRoute<T> {
582+
/// Default const constructor
583+
const TypedRelativeGoRoute({
584+
required this.path,
585+
this.routes = const <TypedRoute<RouteData>>[],
586+
this.caseSensitive = true,
587+
});
588+
589+
/// The relative path that corresponds to this route.
590+
///
591+
/// See [GoRoute.path].
592+
///
593+
///
594+
final String path;
595+
596+
/// Child route definitions.
597+
///
598+
/// See [RouteBase.routes].
599+
final List<TypedRoute<RouteData>> routes;
600+
601+
/// Determines whether the route matching is case sensitive.
602+
///
603+
/// When `true`, the path must match the specified case. For example,
604+
/// a route with `path: '/family/:fid'` will not match `/FaMiLy/f2`.
605+
///
606+
/// When `false`, the path matching is case insensitive. The route
607+
/// with `path: '/family/:fid'` will match `/FaMiLy/f2`.
608+
///
609+
/// Defaults to `true`.
610+
final bool caseSensitive;
611+
}
612+
433613
/// A superclass for each typed shell route descendant
434614
@Target(<TargetKind>{TargetKind.library, TargetKind.classType})
435615
class TypedShellRoute<T extends ShellRouteData> extends TypedRoute<T> {

packages/go_router/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: go_router
22
description: A declarative router for Flutter based on Navigation 2 supporting
33
deep linking, data-driven routes and more
4-
version: 16.0.0
4+
version: 16.0.1
55
repository: https://github.com/flutter/packages/tree/main/packages/go_router
66
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22
77

0 commit comments

Comments
 (0)