diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 2587bbd63d80..75e73f888203 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 15.2.0 -* Updates minimum supported SDK version to Flutter 3.27/Dart 3.6. +- Updates minimum supported SDK version to Flutter 3.27/Dart 3.6. +- `GoRouteData` now defines `.location`, `.go(context)`, `.push(context)`, `.pushReplacement(context)`, and `replace(context)` to be used for [Type-safe routing](https://pub.dev/documentation/go_router/latest/topics/Type-safe%20routes-topic.html). **Requires go_router_builder >= 3.0.0**. ## 15.1.2 diff --git a/packages/go_router/lib/src/route_data.dart b/packages/go_router/lib/src/route_data.dart index e288182de8ac..24afed527405 100644 --- a/packages/go_router/lib/src/route_data.dart +++ b/packages/go_router/lib/src/route_data.dart @@ -133,6 +133,30 @@ abstract class GoRouteData extends RouteData { static final Expando _stateObjectExpando = Expando( 'GoRouteState to GoRouteData expando', ); + + /// The location of this route. + String get location => throw _shouldBeGeneratedError; + + /// Navigate to the route. + void go(BuildContext context) => throw _shouldBeGeneratedError; + + /// Push the route onto the page stack. + Future push(BuildContext context) => throw _shouldBeGeneratedError; + + /// Replaces the top-most page of the page stack with the route. + void pushReplacement(BuildContext context) => throw _shouldBeGeneratedError; + + /// Replaces the top-most page of the page stack with the route but treats + /// it as the same page. + /// + /// The page key will be reused. This will preserve the state and not run any + /// page animation. + /// + void replace(BuildContext context) => throw _shouldBeGeneratedError; + + static UnimplementedError get _shouldBeGeneratedError => UnimplementedError( + 'Should be generated using [Type-safe routing](https://pub.dev/documentation/go_router/latest/topics/Type-safe%20routes-topic.html).', + ); } /// A class to represent a [ShellRoute] in diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index 1756625b35d6..9367be85902f 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: 15.1.2 +version: 15.2.0 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 diff --git a/packages/go_router/test/route_data_test.dart b/packages/go_router/test/route_data_test.dart index 7d3ce38bd4b1..83889adfbf44 100644 --- a/packages/go_router/test/route_data_test.dart +++ b/packages/go_router/test/route_data_test.dart @@ -280,6 +280,76 @@ void main() { expect(routeWithDefaultCaseSensitivity.caseSensitive, false); }, ); + + testWidgets( + 'It should throw beacuase there is no code generated', + (WidgetTester tester) async { + final List errors = []; + + FlutterError.onError = + (FlutterErrorDetails details) => errors.add(details); + + const String errorText = 'Should be generated'; + + Widget buildWidget(void Function(BuildContext) onTap) { + return MaterialApp( + home: Builder( + builder: (BuildContext context) => GestureDetector( + child: const Text('Tap'), + onTap: () => onTap(context), + ), + ), + ); + } + + final Widget pushThrower = buildWidget((BuildContext context) { + const _GoRouteDataBuild().push(context); + }); + await tester.pumpWidget(pushThrower); + await tester.tap(find.text('Tap')); + + expect(errors.first.exception, isA()); + expect(errors.first.exception.toString(), contains(errorText)); + + errors.clear(); + + final Widget goThrower = buildWidget((BuildContext context) { + const _GoRouteDataBuild().go(context); + }); + await tester.pumpWidget(goThrower); + await tester.tap(find.text('Tap')); + + expect(errors.first.exception, isA()); + expect(errors.first.exception.toString(), contains(errorText)); + + errors.clear(); + + final Widget pushReplacementThrower = + buildWidget((BuildContext context) { + const _GoRouteDataBuild().pushReplacement(context); + }); + await tester.pumpWidget(pushReplacementThrower); + await tester.tap(find.text('Tap')); + + expect(errors.first.exception, isA()); + expect(errors.first.exception.toString(), contains(errorText)); + + errors.clear(); + + final Widget replaceThrower = buildWidget((BuildContext context) { + const _GoRouteDataBuild().pushReplacement(context); + }); + await tester.pumpWidget(replaceThrower); + await tester.tap(find.text('Tap')); + + expect(errors.first.exception, isA()); + expect(errors.first.exception.toString(), contains(errorText)); + + errors.clear(); + + FlutterError.onError = FlutterError.dumpErrorToConsole; + }, + ); }); group('ShellRouteData', () {