Skip to content

Commit 5113c50

Browse files
authored
improve error message for navigator page api (flutter#73153)
1 parent 119e0ea commit 5113c50

File tree

2 files changed

+146
-12
lines changed

2 files changed

+146
-12
lines changed

packages/flutter/lib/src/widgets/navigator.dart

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3310,10 +3310,35 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
33103310
@override
33113311
void initState() {
33123312
super.initState();
3313-
assert(
3314-
widget.pages.isEmpty || widget.onPopPage != null,
3315-
'The Navigator.onPopPage must be provided to use the Navigator.pages API',
3316-
);
3313+
assert((){
3314+
if (widget.pages != const <Page<dynamic>>[]) {
3315+
// This navigator uses page API.
3316+
if (widget.pages.isEmpty) {
3317+
FlutterError.reportError(
3318+
FlutterErrorDetails(
3319+
exception: FlutterError(
3320+
'The Navigator.pages must not be empty to use the '
3321+
'Navigator.pages API'
3322+
),
3323+
library: 'widget library',
3324+
stack: StackTrace.current,
3325+
),
3326+
);
3327+
} else if (widget.onPopPage == null) {
3328+
FlutterError.reportError(
3329+
FlutterErrorDetails(
3330+
exception: FlutterError(
3331+
'The Navigator.onPopPage must be provided to use the '
3332+
'Navigator.pages API'
3333+
),
3334+
library: 'widget library',
3335+
stack: StackTrace.current,
3336+
),
3337+
);
3338+
}
3339+
}
3340+
return true;
3341+
}());
33173342
for (final NavigatorObserver observer in widget.observers) {
33183343
assert(observer.navigator == null);
33193344
observer._navigator = this;
@@ -3480,10 +3505,35 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
34803505
@override
34813506
void didUpdateWidget(Navigator oldWidget) {
34823507
super.didUpdateWidget(oldWidget);
3483-
assert(
3484-
widget.pages.isEmpty || widget.onPopPage != null,
3485-
'The Navigator.onPopPage must be provided to use the Navigator.pages API',
3486-
);
3508+
assert((){
3509+
if (widget.pages != const <Page<dynamic>>[]) {
3510+
// This navigator uses page API.
3511+
if (widget.pages.isEmpty) {
3512+
FlutterError.reportError(
3513+
FlutterErrorDetails(
3514+
exception: FlutterError(
3515+
'The Navigator.pages must not be empty to use the '
3516+
'Navigator.pages API'
3517+
),
3518+
library: 'widget library',
3519+
stack: StackTrace.current,
3520+
),
3521+
);
3522+
} else if (widget.onPopPage == null) {
3523+
FlutterError.reportError(
3524+
FlutterErrorDetails(
3525+
exception: FlutterError(
3526+
'The Navigator.onPopPage must be provided to use the '
3527+
'Navigator.pages API'
3528+
),
3529+
library: 'widget library',
3530+
stack: StackTrace.current,
3531+
),
3532+
);
3533+
}
3534+
}
3535+
return true;
3536+
}());
34873537
if (oldWidget.observers != widget.observers) {
34883538
for (final NavigatorObserver observer in oldWidget.observers)
34893539
observer._navigator = null;
@@ -3494,10 +3544,21 @@ class NavigatorState extends State<Navigator> with TickerProviderStateMixin, Res
34943544
_updateEffectiveObservers();
34953545
}
34963546
if (oldWidget.pages != widget.pages && !restorePending) {
3497-
assert(
3498-
widget.pages.isNotEmpty,
3499-
'To use the Navigator.pages, there must be at least one page in the list.'
3500-
);
3547+
assert((){
3548+
if (widget.pages.isEmpty) {
3549+
FlutterError.reportError(
3550+
FlutterErrorDetails(
3551+
exception: FlutterError(
3552+
'The Navigator.pages must not be empty to use the '
3553+
'Navigator.pages API'
3554+
),
3555+
library: 'widget library',
3556+
stack: StackTrace.current,
3557+
),
3558+
);
3559+
}
3560+
return true;
3561+
}());
35013562
_updatePages();
35023563
}
35033564

packages/flutter/test/widgets/navigator_test.dart

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,6 +2608,79 @@ void main() {
26082608
expect(find.text('initial'), findsOneWidget);
26092609
});
26102610

2611+
testWidgets('throw if onPopPage callback is not provided', (WidgetTester tester) async {
2612+
final List<TestPage> myPages = <TestPage>[
2613+
const TestPage(key: ValueKey<String>('1'), name:'initial'),
2614+
const TestPage(key: ValueKey<String>('2'), name:'second'),
2615+
const TestPage(key: ValueKey<String>('3'), name:'third'),
2616+
];
2617+
2618+
await tester.pumpWidget(
2619+
MediaQuery(
2620+
data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window),
2621+
child: Localizations(
2622+
locale: const Locale('en', 'US'),
2623+
delegates: const <LocalizationsDelegate<dynamic>>[
2624+
DefaultMaterialLocalizations.delegate,
2625+
DefaultWidgetsLocalizations.delegate
2626+
],
2627+
child: Directionality(
2628+
textDirection: TextDirection.ltr,
2629+
child: Navigator(
2630+
pages: myPages,
2631+
),
2632+
),
2633+
),
2634+
)
2635+
);
2636+
2637+
final dynamic exception = tester.takeException();
2638+
expect(exception, isFlutterError);
2639+
final FlutterError error = exception as FlutterError;
2640+
expect(
2641+
error.toStringDeep(),
2642+
equalsIgnoringHashCodes(
2643+
'FlutterError\n'
2644+
' The Navigator.onPopPage must be provided to use the\n'
2645+
' Navigator.pages API\n'
2646+
''
2647+
),
2648+
);
2649+
});
2650+
2651+
testWidgets('throw if page list is empty', (WidgetTester tester) async {
2652+
final List<TestPage> myPages = <TestPage>[];
2653+
final FlutterExceptionHandler? originalOnError = FlutterError.onError;
2654+
FlutterErrorDetails? firstError;
2655+
FlutterError.onError = (FlutterErrorDetails? detail) {
2656+
// We only care about the first error;
2657+
firstError ??= detail;
2658+
};
2659+
await tester.pumpWidget(
2660+
MediaQuery(
2661+
data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window),
2662+
child: Localizations(
2663+
locale: const Locale('en', 'US'),
2664+
delegates: const <LocalizationsDelegate<dynamic>>[
2665+
DefaultMaterialLocalizations.delegate,
2666+
DefaultWidgetsLocalizations.delegate
2667+
],
2668+
child: Directionality(
2669+
textDirection: TextDirection.ltr,
2670+
child: Navigator(
2671+
pages: myPages,
2672+
),
2673+
),
2674+
),
2675+
)
2676+
);
2677+
FlutterError.onError = originalOnError;
2678+
expect(
2679+
firstError!.exception.toString(),
2680+
'The Navigator.pages must not be empty to use the Navigator.pages API',
2681+
);
2682+
});
2683+
26112684
testWidgets('can push and pop pages using page api', (WidgetTester tester) async {
26122685
late Animation<double> secondaryAnimationOfRouteOne;
26132686
late Animation<double> primaryAnimationOfRouteOne;

0 commit comments

Comments
 (0)