diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index 89389b3285..0400f847a2 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -4450,10 +4450,30 @@ class NavigatorState extends State with TickerProviderStateMixin, Res /// state restoration. @optionalTypeArgs Future push(Route route) { + assert(_debugCheckIsPagelessRoute(route)); _pushEntry(_RouteEntry(route, initialState: _RouteLifecycle.push)); return route.popped; } + bool _debugCheckIsPagelessRoute(Route route) { + assert((){ + if (route.settings is Page) { + FlutterError.reportError( + FlutterErrorDetails( + exception: FlutterError( + 'A page-based route should not be added using the imperative api. ' + 'Provide a new list with the corresponding Page to Navigator.pages instead.' + ), + library: 'widget library', + stack: StackTrace.current, + ), + ); + } + return true; + }()); + return true; + } + bool _debugIsStaticCallback(Function callback) { bool result = false; assert(() { @@ -4596,6 +4616,7 @@ class NavigatorState extends State with TickerProviderStateMixin, Res Future pushReplacement(Route newRoute, { TO? result }) { assert(newRoute != null); assert(newRoute._navigator == null); + assert(_debugCheckIsPagelessRoute(newRoute)); _pushReplacementEntry(_RouteEntry(newRoute, initialState: _RouteLifecycle.pushReplace), result); return newRoute.popped; } @@ -4703,6 +4724,7 @@ class NavigatorState extends State with TickerProviderStateMixin, Res assert(newRoute != null); assert(newRoute._navigator == null); assert(newRoute.overlayEntries.isEmpty); + assert(_debugCheckIsPagelessRoute(newRoute)); _pushEntryAndRemoveUntil(_RouteEntry(newRoute, initialState: _RouteLifecycle.push), predicate); return newRoute.popped; } diff --git a/packages/flutter/test/widgets/navigator_test.dart b/packages/flutter/test/widgets/navigator_test.dart index 22faf4797a..a5aadc7100 100644 --- a/packages/flutter/test/widgets/navigator_test.dart +++ b/packages/flutter/test/widgets/navigator_test.dart @@ -2648,6 +2648,63 @@ void main() { ); }); + Widget _buildFrame(String action) { + const TestPage myPage = TestPage(key: ValueKey('1'), name:'initial'); + final Map routes = { + '/' : (BuildContext context) => OnTapPage( + id: action, + onTap: (){ + if (action == 'push') { + Navigator.of(context).push(myPage.createRoute(context)); + } else if (action == 'pushReplacement') { + Navigator.of(context).pushReplacement(myPage.createRoute(context)); + } else if (action == 'pushAndRemoveUntil') { + Navigator.of(context).pushAndRemoveUntil(myPage.createRoute(context), (_) => true); + } + }, + ), + }; + + return MaterialApp(routes: routes); + } + + void _checkException(WidgetTester tester) { + final dynamic exception = tester.takeException(); + expect(exception, isFlutterError); + final FlutterError error = exception as FlutterError; + expect( + error.toStringDeep(), + equalsIgnoringHashCodes( + 'FlutterError\n' + ' A page-based route should not be added using the imperative api.\n' + ' Provide a new list with the corresponding Page to Navigator.pages\n' + ' instead.\n' + '' + ), + ); + } + + testWidgets('throw if add page-based route using the imperative api - push', (WidgetTester tester) async { + await tester.pumpWidget(_buildFrame('push')); + await tester.tap(find.text('push')); + await tester.pumpAndSettle(); + _checkException(tester); + }); + + testWidgets('throw if add page-based route using the imperative api - pushReplacement', (WidgetTester tester) async { + await tester.pumpWidget(_buildFrame('pushReplacement')); + await tester.tap(find.text('pushReplacement')); + await tester.pumpAndSettle(); + _checkException(tester); + }); + + testWidgets('throw if add page-based route using the imperative api - pushAndRemoveUntil', (WidgetTester tester) async { + await tester.pumpWidget(_buildFrame('pushAndRemoveUntil')); + await tester.tap(find.text('pushAndRemoveUntil')); + await tester.pumpAndSettle(); + _checkException(tester); + }); + testWidgets('throw if page list is empty', (WidgetTester tester) async { final List myPages = []; final FlutterExceptionHandler? originalOnError = FlutterError.onError;