@@ -134,7 +134,7 @@ abstract class GoRouteData extends RouteData {
134
134
'GoRouteState to GoRouteData expando' ,
135
135
);
136
136
137
- /// The location of this route.
137
+ /// The location of this route, e.g. /family/f2/person/p1
138
138
String get location => throw _shouldBeGeneratedError;
139
139
140
140
/// Navigate to the route.
@@ -159,6 +159,151 @@ abstract class GoRouteData extends RouteData {
159
159
);
160
160
}
161
161
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
+
162
307
/// A class to represent a [ShellRoute] in
163
308
/// [Type-safe routing] (https://pub.dev/documentation/go_router/latest/topics/Type-safe%20routes-topic.html).
164
309
abstract class ShellRouteData extends RouteData {
@@ -430,6 +575,41 @@ class TypedGoRoute<T extends GoRouteData> extends TypedRoute<T> {
430
575
final bool caseSensitive;
431
576
}
432
577
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
+
433
613
/// A superclass for each typed shell route descendant
434
614
@Target (< TargetKind > {TargetKind .library, TargetKind .classType})
435
615
class TypedShellRoute <T extends ShellRouteData > extends TypedRoute <T > {
0 commit comments