Add "navigator" option to "showDialog" and "showGeneralDialog" (#42842)
This commit is contained in:
@@ -864,6 +864,10 @@ class _CupertinoModalPopupRoute<T> extends PopupRoute<T> {
|
||||
/// It is only used when the method is called. Its corresponding widget can be
|
||||
/// safely removed from the tree before the popup is closed.
|
||||
///
|
||||
/// The `useRootNavigator` argument is used to determine whether to push the
|
||||
/// popup to the [Navigator] furthest from or nearest to the given `context`. It
|
||||
/// is `false` by default.
|
||||
///
|
||||
/// The `builder` argument typically builds a [CupertinoActionSheet] widget.
|
||||
/// Content below the widget is dimmed with a [ModalBarrier]. The widget built
|
||||
/// by the `builder` does not share a context with the location that
|
||||
@@ -882,8 +886,10 @@ class _CupertinoModalPopupRoute<T> extends PopupRoute<T> {
|
||||
Future<T> showCupertinoModalPopup<T>({
|
||||
@required BuildContext context,
|
||||
@required WidgetBuilder builder,
|
||||
bool useRootNavigator = true,
|
||||
}) {
|
||||
return Navigator.of(context, rootNavigator: true).push(
|
||||
assert(useRootNavigator != null);
|
||||
return Navigator.of(context, rootNavigator: useRootNavigator).push(
|
||||
_CupertinoModalPopupRoute<T>(
|
||||
builder: builder,
|
||||
barrierLabel: 'Dismiss',
|
||||
@@ -933,14 +939,18 @@ Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double>
|
||||
/// It is only used when the method is called. Its corresponding widget can
|
||||
/// be safely removed from the tree before the dialog is closed.
|
||||
///
|
||||
/// Returns a [Future] that resolves to the value (if any) that was passed to
|
||||
/// [Navigator.pop] when the dialog was closed.
|
||||
/// The `useRootNavigator` argument is used to determine whether to push the
|
||||
/// dialog to the [Navigator] furthest from or nearest to the given `context`.
|
||||
/// By default, `useRootNavigator` is `true` and the dialog route created by
|
||||
/// this method is pushed to the root navigator.
|
||||
///
|
||||
/// The dialog route created by this method is pushed to the root navigator.
|
||||
/// If the application has multiple [Navigator] objects, it may be necessary to
|
||||
/// call `Navigator.of(context, rootNavigator: true).pop(result)` to close the
|
||||
/// dialog rather than just `Navigator.pop(context, result)`.
|
||||
///
|
||||
/// Returns a [Future] that resolves to the value (if any) that was passed to
|
||||
/// [Navigator.pop] when the dialog was closed.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [CupertinoDialog], an iOS-style dialog.
|
||||
@@ -951,8 +961,10 @@ Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double>
|
||||
Future<T> showCupertinoDialog<T>({
|
||||
@required BuildContext context,
|
||||
@required WidgetBuilder builder,
|
||||
bool useRootNavigator = true,
|
||||
}) {
|
||||
assert(builder != null);
|
||||
assert(useRootNavigator != null);
|
||||
return showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
@@ -963,5 +975,6 @@ Future<T> showCupertinoDialog<T>({
|
||||
return builder(context);
|
||||
},
|
||||
transitionBuilder: _buildCupertinoDialogTransitions,
|
||||
useRootNavigator: useRootNavigator,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -223,8 +223,8 @@ class AboutListTile extends StatelessWidget {
|
||||
/// The licenses shown on the [LicensePage] are those returned by the
|
||||
/// [LicenseRegistry] API, which can be used to add more licenses to the list.
|
||||
///
|
||||
/// The `context` argument is passed to [showDialog], the documentation for
|
||||
/// which discusses how it is used.
|
||||
/// The [context] and [useRootNavigator] arguments are passed to [showDialog],
|
||||
/// the documentation for which discusses how it is used.
|
||||
void showAboutDialog({
|
||||
@required BuildContext context,
|
||||
String applicationName,
|
||||
@@ -232,10 +232,13 @@ void showAboutDialog({
|
||||
Widget applicationIcon,
|
||||
String applicationLegalese,
|
||||
List<Widget> children,
|
||||
bool useRootNavigator = true,
|
||||
}) {
|
||||
assert(context != null);
|
||||
assert(useRootNavigator != null);
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
useRootNavigator: useRootNavigator,
|
||||
builder: (BuildContext context) {
|
||||
return AboutDialog(
|
||||
applicationName: applicationName,
|
||||
@@ -251,7 +254,13 @@ void showAboutDialog({
|
||||
/// Displays a [LicensePage], which shows licenses for software used by the
|
||||
/// application.
|
||||
///
|
||||
/// The arguments correspond to the properties on [LicensePage].
|
||||
/// The application arguments correspond to the properties on [LicensePage].
|
||||
///
|
||||
/// The `context` argument is used to look up the [Navigator] for the page.
|
||||
///
|
||||
/// The `useRootNavigator` argument is used to determine whether to push the
|
||||
/// page to the [Navigator] furthest from or nearest to the given `context`. It
|
||||
/// is `false` by default.
|
||||
///
|
||||
/// If the application has a [Drawer], consider using [AboutListTile] instead
|
||||
/// of calling this directly.
|
||||
@@ -267,9 +276,11 @@ void showLicensePage({
|
||||
String applicationVersion,
|
||||
Widget applicationIcon,
|
||||
String applicationLegalese,
|
||||
bool useRootNavigator = false,
|
||||
}) {
|
||||
assert(context != null);
|
||||
Navigator.push(context, MaterialPageRoute<void>(
|
||||
assert(useRootNavigator != null);
|
||||
Navigator.of(context, rootNavigator: useRootNavigator).push(MaterialPageRoute<void>(
|
||||
builder: (BuildContext context) => LicensePage(
|
||||
applicationName: applicationName,
|
||||
applicationVersion: applicationVersion,
|
||||
|
||||
@@ -1085,8 +1085,8 @@ typedef SelectableDayPredicate = bool Function(DateTime day);
|
||||
/// provided by [Directionality]. If both [locale] and [textDirection] are not
|
||||
/// null, [textDirection] overrides the direction chosen for the [locale].
|
||||
///
|
||||
/// The [context] argument is passed to [showDialog], the documentation for
|
||||
/// which discusses how it is used.
|
||||
/// The [context] and [useRootNavigator] arguments are passed to [showDialog],
|
||||
/// the documentation for which discusses how it is used.
|
||||
///
|
||||
/// The [builder] parameter can be used to wrap the dialog widget
|
||||
/// to add inherited widgets like [Theme].
|
||||
@@ -1133,10 +1133,12 @@ Future<DateTime> showDatePicker({
|
||||
Locale locale,
|
||||
TextDirection textDirection,
|
||||
TransitionBuilder builder,
|
||||
bool useRootNavigator = true,
|
||||
}) async {
|
||||
assert(initialDate != null);
|
||||
assert(firstDate != null);
|
||||
assert(lastDate != null);
|
||||
assert(useRootNavigator != null);
|
||||
assert(!initialDate.isBefore(firstDate), 'initialDate must be on or after firstDate');
|
||||
assert(!initialDate.isAfter(lastDate), 'initialDate must be on or before lastDate');
|
||||
assert(!firstDate.isAfter(lastDate), 'lastDate must be on or after firstDate');
|
||||
@@ -1173,6 +1175,7 @@ Future<DateTime> showDatePicker({
|
||||
|
||||
return await showDialog<DateTime>(
|
||||
context: context,
|
||||
useRootNavigator: useRootNavigator,
|
||||
builder: (BuildContext context) {
|
||||
return builder == null ? child : builder(context, child);
|
||||
},
|
||||
|
||||
@@ -655,20 +655,24 @@ Widget _buildMaterialDialogTransitions(BuildContext context, Animation<double> a
|
||||
/// `showDialog` is originally called from. Use a [StatefulBuilder] or a
|
||||
/// custom [StatefulWidget] if the dialog needs to update dynamically.
|
||||
///
|
||||
/// The `child` argument is deprecated, and should be replaced with `builder`.
|
||||
///
|
||||
/// The `context` argument is used to look up the [Navigator] and [Theme] for
|
||||
/// the dialog. It is only used when the method is called. Its corresponding
|
||||
/// widget can be safely removed from the tree before the dialog is closed.
|
||||
///
|
||||
/// The `child` argument is deprecated, and should be replaced with `builder`.
|
||||
/// The `useRootNavigator` argument is used to determine whether to push the
|
||||
/// dialog to the [Navigator] furthest from or nearest to the given `context`.
|
||||
/// By default, `useRootNavigator` is `true` and the dialog route created by
|
||||
/// this method is pushed to the root navigator.
|
||||
///
|
||||
/// Returns a [Future] that resolves to the value (if any) that was passed to
|
||||
/// [Navigator.pop] when the dialog was closed.
|
||||
///
|
||||
/// The dialog route created by this method is pushed to the root navigator.
|
||||
/// If the application has multiple [Navigator] objects, it may be necessary to
|
||||
/// call `Navigator.of(context, rootNavigator: true).pop(result)` to close the
|
||||
/// dialog rather than just `Navigator.pop(context, result)`.
|
||||
///
|
||||
/// Returns a [Future] that resolves to the value (if any) that was passed to
|
||||
/// [Navigator.pop] when the dialog was closed.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [AlertDialog], for dialogs that have a row of buttons below a body.
|
||||
@@ -687,8 +691,10 @@ Future<T> showDialog<T>({
|
||||
'is appropriate for widgets built in the dialog.'
|
||||
) Widget child,
|
||||
WidgetBuilder builder,
|
||||
bool useRootNavigator = true,
|
||||
}) {
|
||||
assert(child == null || builder == null);
|
||||
assert(useRootNavigator != null);
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
|
||||
final ThemeData theme = Theme.of(context, shadowThemeOnly: true);
|
||||
@@ -711,5 +717,6 @@ Future<T> showDialog<T>({
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration: const Duration(milliseconds: 150),
|
||||
transitionBuilder: _buildMaterialDialogTransitions,
|
||||
useRootNavigator: useRootNavigator,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -790,6 +790,10 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
|
||||
/// the menu. It is only used when the method is called. Its corresponding
|
||||
/// widget can be safely removed from the tree before the popup menu is closed.
|
||||
///
|
||||
/// The `useRootNavigator` argument is used to determine whether to push the
|
||||
/// menu to the [Navigator] furthest from or nearest to the given `context`. It
|
||||
/// is `false` by default.
|
||||
///
|
||||
/// The `semanticLabel` argument is used by accessibility frameworks to
|
||||
/// announce screen transitions when the menu is opened and closed. If this
|
||||
/// label is not provided, it will default to
|
||||
@@ -814,9 +818,11 @@ Future<T> showMenu<T>({
|
||||
ShapeBorder shape,
|
||||
Color color,
|
||||
bool captureInheritedThemes = true,
|
||||
bool useRootNavigator = false,
|
||||
}) {
|
||||
assert(context != null);
|
||||
assert(position != null);
|
||||
assert(useRootNavigator != null);
|
||||
assert(items != null && items.isNotEmpty);
|
||||
assert(captureInheritedThemes != null);
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
@@ -831,7 +837,7 @@ Future<T> showMenu<T>({
|
||||
label = semanticLabel ?? MaterialLocalizations.of(context)?.popupMenuLabel;
|
||||
}
|
||||
|
||||
return Navigator.push(context, _PopupMenuRoute<T>(
|
||||
return Navigator.of(context, rootNavigator: useRootNavigator).push(_PopupMenuRoute<T>(
|
||||
position: position,
|
||||
items: items,
|
||||
initialValue: initialValue,
|
||||
|
||||
@@ -1731,8 +1731,8 @@ class _TimePickerDialogState extends State<_TimePickerDialog> {
|
||||
/// ```
|
||||
/// {@end-tool}
|
||||
///
|
||||
/// The [context] argument is passed to [showDialog], the documentation for
|
||||
/// which discusses how it is used.
|
||||
/// The [context] and [useRootNavigator] arguments are passed to [showDialog],
|
||||
/// the documentation for which discusses how it is used.
|
||||
///
|
||||
/// The [builder] parameter can be used to wrap the dialog widget
|
||||
/// to add inherited widgets like [Localizations.override],
|
||||
@@ -1780,14 +1780,17 @@ Future<TimeOfDay> showTimePicker({
|
||||
@required BuildContext context,
|
||||
@required TimeOfDay initialTime,
|
||||
TransitionBuilder builder,
|
||||
bool useRootNavigator = true,
|
||||
}) async {
|
||||
assert(context != null);
|
||||
assert(initialTime != null);
|
||||
assert(useRootNavigator != null);
|
||||
assert(debugCheckHasMaterialLocalizations(context));
|
||||
|
||||
final Widget dialog = _TimePickerDialog(initialTime: initialTime);
|
||||
return await showDialog<TimeOfDay>(
|
||||
context: context,
|
||||
useRootNavigator: useRootNavigator,
|
||||
builder: (BuildContext context) {
|
||||
return builder == null ? dialog : builder(context, dialog);
|
||||
},
|
||||
|
||||
@@ -1560,9 +1560,18 @@ class _DialogRoute<T> extends PopupRoute<T> {
|
||||
/// [StatefulWidget] if the dialog needs to update dynamically. The
|
||||
/// `pageBuilder` argument can not be null.
|
||||
///
|
||||
/// The `context` argument is used to look up the [Navigator] for the dialog.
|
||||
/// It is only used when the method is called. Its corresponding widget can
|
||||
/// be safely removed from the tree before the dialog is closed.
|
||||
/// The `context` argument is used to look up the [Navigator] for the
|
||||
/// dialog. It is only used when the method is called. Its corresponding widget
|
||||
/// can be safely removed from the tree before the dialog is closed.
|
||||
///
|
||||
/// The `useRootNavigator` argument is used to determine whether to push the
|
||||
/// dialog to the [Navigator] furthest from or nearest to the given `context`.
|
||||
/// By default, `useRootNavigator` is `true` and the dialog route created by
|
||||
/// this method is pushed to the root navigator.
|
||||
///
|
||||
/// If the application has multiple [Navigator] objects, it may be necessary to
|
||||
/// call `Navigator.of(context, rootNavigator: true).pop(result)` to close the
|
||||
/// dialog rather than just `Navigator.pop(context, result)`.
|
||||
///
|
||||
/// The `barrierDismissible` argument is used to determine whether this route
|
||||
/// can be dismissed by tapping the modal barrier. This argument defaults
|
||||
@@ -1586,11 +1595,6 @@ class _DialogRoute<T> extends PopupRoute<T> {
|
||||
/// Returns a [Future] that resolves to the value (if any) that was passed to
|
||||
/// [Navigator.pop] when the dialog was closed.
|
||||
///
|
||||
/// The dialog route created by this method is pushed to the root navigator.
|
||||
/// If the application has multiple [Navigator] objects, it may be necessary to
|
||||
/// call `Navigator.of(context, rootNavigator: true).pop(result)` to close the
|
||||
/// dialog rather than just `Navigator.pop(context, result)`.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [showDialog], which displays a Material-style dialog.
|
||||
@@ -1603,10 +1607,12 @@ Future<T> showGeneralDialog<T>({
|
||||
Color barrierColor,
|
||||
Duration transitionDuration,
|
||||
RouteTransitionsBuilder transitionBuilder,
|
||||
bool useRootNavigator = true,
|
||||
}) {
|
||||
assert(pageBuilder != null);
|
||||
assert(useRootNavigator != null);
|
||||
assert(!barrierDismissible || barrierLabel != null);
|
||||
return Navigator.of(context, rootNavigator: true).push<T>(_DialogRoute<T>(
|
||||
return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(_DialogRoute<T>(
|
||||
pageBuilder: pageBuilder,
|
||||
barrierDismissible: barrierDismissible,
|
||||
barrierLabel: barrierLabel,
|
||||
|
||||
@@ -918,6 +918,164 @@ void main() {
|
||||
expect(homeTapCount, 1);
|
||||
expect(pageTapCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('showCupertinoModalPopup uses root navigator by default', (WidgetTester tester) async {
|
||||
final PopupObserver rootObserver = PopupObserver();
|
||||
final PopupObserver nestedObserver = PopupObserver();
|
||||
|
||||
await tester.pumpWidget(CupertinoApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return PageRouteBuilder<dynamic>(
|
||||
pageBuilder: (BuildContext context, Animation<double> _, Animation<double> __) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
await showCupertinoModalPopup<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => const SizedBox(),
|
||||
);
|
||||
},
|
||||
child: const Text('tap'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.text('tap'));
|
||||
|
||||
expect(rootObserver.popupCount, 1);
|
||||
expect(nestedObserver.popupCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('showCupertinoModalPopup uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||
final PopupObserver rootObserver = PopupObserver();
|
||||
final PopupObserver nestedObserver = PopupObserver();
|
||||
|
||||
await tester.pumpWidget(CupertinoApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return PageRouteBuilder<dynamic>(
|
||||
pageBuilder: (BuildContext context, Animation<double> _, Animation<double> __) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
await showCupertinoModalPopup<void>(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (BuildContext context) => const SizedBox(),
|
||||
);
|
||||
},
|
||||
child: const Text('tap'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.text('tap'));
|
||||
|
||||
expect(rootObserver.popupCount, 0);
|
||||
expect(nestedObserver.popupCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('showCupertinoDialog uses root navigator by default', (WidgetTester tester) async {
|
||||
final DialogObserver rootObserver = DialogObserver();
|
||||
final DialogObserver nestedObserver = DialogObserver();
|
||||
|
||||
await tester.pumpWidget(CupertinoApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return PageRouteBuilder<dynamic>(
|
||||
pageBuilder: (BuildContext context, Animation<double> _, Animation<double> __) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
await showCupertinoDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => const SizedBox(),
|
||||
);
|
||||
},
|
||||
child: const Text('tap'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.text('tap'));
|
||||
|
||||
expect(rootObserver.dialogCount, 1);
|
||||
expect(nestedObserver.dialogCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('showCupertinoDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||
final DialogObserver rootObserver = DialogObserver();
|
||||
final DialogObserver nestedObserver = DialogObserver();
|
||||
|
||||
await tester.pumpWidget(CupertinoApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return PageRouteBuilder<dynamic>(
|
||||
pageBuilder: (BuildContext context, Animation<double> _, Animation<double> __) {
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
await showCupertinoDialog<void>(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (BuildContext context) => const SizedBox(),
|
||||
);
|
||||
},
|
||||
child: const Text('tap'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.text('tap'));
|
||||
|
||||
expect(rootObserver.dialogCount, 0);
|
||||
expect(nestedObserver.dialogCount, 1);
|
||||
});
|
||||
}
|
||||
|
||||
class MockNavigatorObserver extends Mock implements NavigatorObserver {}
|
||||
|
||||
class PopupObserver extends NavigatorObserver {
|
||||
int popupCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route.toString().contains('_CupertinoModalPopupRoute')) {
|
||||
popupCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
class DialogObserver extends NavigatorObserver {
|
||||
int dialogCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route.toString().contains('_DialogRoute')) {
|
||||
dialogCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +305,150 @@ void main() {
|
||||
tileRect = tester.getRect(find.byType(AboutListTile));
|
||||
expect(tileRect.height, 48.0);
|
||||
});
|
||||
|
||||
testWidgets('showLicensePage uses nested navigator by default', (WidgetTester tester) async {
|
||||
final LicensePageObserver rootObserver = LicensePageObserver();
|
||||
final LicensePageObserver nestedObserver = LicensePageObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (_) {
|
||||
return PageRouteBuilder<dynamic>(
|
||||
pageBuilder: (_, __, ___) => Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return PageRouteBuilder<dynamic>(
|
||||
pageBuilder: (BuildContext context, _, __) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showLicensePage(
|
||||
context: context,
|
||||
applicationName: 'A',
|
||||
);
|
||||
},
|
||||
child: const Text('Show License Page'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.licensePageCount, 0);
|
||||
expect(nestedObserver.licensePageCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('showLicensePage uses root navigator if useRootNavigator is true', (WidgetTester tester) async {
|
||||
final LicensePageObserver rootObserver = LicensePageObserver();
|
||||
final LicensePageObserver nestedObserver = LicensePageObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
initialRoute: '/',
|
||||
onGenerateRoute: (_) {
|
||||
return PageRouteBuilder<dynamic>(
|
||||
pageBuilder: (_, __, ___) => Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return PageRouteBuilder<dynamic>(
|
||||
pageBuilder: (BuildContext context, _, __) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showLicensePage(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
applicationName: 'A',
|
||||
);
|
||||
},
|
||||
child: const Text('Show License Page'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.licensePageCount, 1);
|
||||
expect(nestedObserver.licensePageCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('showAboutDialog uses root navigator by default', (WidgetTester tester) async {
|
||||
final AboutDialogObserver rootObserver = AboutDialogObserver();
|
||||
final AboutDialogObserver nestedObserver = AboutDialogObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showAboutDialog(
|
||||
context: context,
|
||||
applicationName: 'A',
|
||||
);
|
||||
},
|
||||
child: const Text('Show About Dialog'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.dialogCount, 1);
|
||||
expect(nestedObserver.dialogCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('showAboutDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||
final AboutDialogObserver rootObserver = AboutDialogObserver();
|
||||
final AboutDialogObserver nestedObserver = AboutDialogObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showAboutDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
applicationName: 'A',
|
||||
);
|
||||
},
|
||||
child: const Text('Show About Dialog'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.dialogCount, 0);
|
||||
expect(nestedObserver.dialogCount, 1);
|
||||
});
|
||||
}
|
||||
|
||||
class FakeLicenseEntry extends LicenseEntry {
|
||||
@@ -322,3 +466,27 @@ class FakeLicenseEntry extends LicenseEntry {
|
||||
return <LicenseParagraph>[];
|
||||
}
|
||||
}
|
||||
|
||||
class LicensePageObserver extends NavigatorObserver {
|
||||
int licensePageCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route is MaterialPageRoute<dynamic>) {
|
||||
licensePageCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
class AboutDialogObserver extends NavigatorObserver {
|
||||
int dialogCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route.toString().contains('_DialogRoute')) {
|
||||
dialogCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -897,4 +897,90 @@ void _tests() {
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('uses root navigator by default', (WidgetTester tester) async {
|
||||
final DatePickerObserver rootObserver = DatePickerObserver();
|
||||
final DatePickerObserver nestedObserver = DatePickerObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showDatePicker(
|
||||
context: context,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(2018),
|
||||
lastDate: DateTime(2030),
|
||||
builder: (BuildContext context, Widget child) {
|
||||
return const SizedBox();
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('Show Date Picker'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.datePickerCount, 1);
|
||||
expect(nestedObserver.datePickerCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||
final DatePickerObserver rootObserver = DatePickerObserver();
|
||||
final DatePickerObserver nestedObserver = DatePickerObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showDatePicker(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
initialDate: DateTime.now(),
|
||||
firstDate: DateTime(2018),
|
||||
lastDate: DateTime(2030),
|
||||
builder: (BuildContext context, Widget child) => const SizedBox(),
|
||||
);
|
||||
},
|
||||
child: const Text('Show Date Picker'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.datePickerCount, 0);
|
||||
expect(nestedObserver.datePickerCount, 1);
|
||||
});
|
||||
}
|
||||
|
||||
class DatePickerObserver extends NavigatorObserver {
|
||||
int datePickerCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route.toString().contains('_DialogRoute')) {
|
||||
datePickerCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -646,6 +646,77 @@ void main() {
|
||||
await tester.pump();
|
||||
});
|
||||
|
||||
testWidgets('showDialog uses root navigator by default', (WidgetTester tester) async {
|
||||
final DialogObserver rootObserver = DialogObserver();
|
||||
final DialogObserver nestedObserver = DialogObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext innerContext) {
|
||||
return const AlertDialog(title: Text('Title'));
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('Show Dialog'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.dialogCount, 1);
|
||||
expect(nestedObserver.dialogCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('showDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||
final DialogObserver rootObserver = DialogObserver();
|
||||
final DialogObserver nestedObserver = DialogObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (BuildContext innerContext) {
|
||||
return const AlertDialog(title: Text('Title'));
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('Show Dialog'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.dialogCount, 0);
|
||||
expect(nestedObserver.dialogCount, 1);
|
||||
});
|
||||
|
||||
group('Scrollable title and content', () {
|
||||
testWidgets('Title is scrollable', (WidgetTester tester) async {
|
||||
final Key titleKey = UniqueKey();
|
||||
@@ -723,3 +794,15 @@ void main() {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class DialogObserver extends NavigatorObserver {
|
||||
int dialogCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route.toString().contains('_DialogRoute')) {
|
||||
dialogCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1114,6 +1114,83 @@ void main() {
|
||||
|
||||
expect(find.text('PopupMenuButton icon'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('showMenu uses nested navigator by default', (WidgetTester tester) async {
|
||||
final MenuObserver rootObserver = MenuObserver();
|
||||
final MenuObserver nestedObserver = MenuObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showMenu<int>(
|
||||
context: context,
|
||||
position: const RelativeRect.fromLTRB(0, 0, 0, 0),
|
||||
items: <PopupMenuItem<int>>[
|
||||
const PopupMenuItem<int>(
|
||||
value: 1, child: Text('1'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: const Text('Show Menu'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.menuCount, 0);
|
||||
expect(nestedObserver.menuCount, 1);
|
||||
});
|
||||
|
||||
testWidgets('showMenu uses root navigator if useRootNavigator is true', (WidgetTester tester) async {
|
||||
final MenuObserver rootObserver = MenuObserver();
|
||||
final MenuObserver nestedObserver = MenuObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showMenu<int>(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
position: const RelativeRect.fromLTRB(0, 0, 0, 0),
|
||||
items: <PopupMenuItem<int>>[
|
||||
const PopupMenuItem<int>(
|
||||
value: 1, child: Text('1'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
child: const Text('Show Menu'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.menuCount, 1);
|
||||
expect(nestedObserver.menuCount, 0);
|
||||
});
|
||||
}
|
||||
|
||||
class TestApp extends StatefulWidget {
|
||||
@@ -1153,3 +1230,15 @@ class _TestAppState extends State<TestApp> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MenuObserver extends NavigatorObserver {
|
||||
int menuCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route.toString().contains('_PopupMenuRoute')) {
|
||||
menuCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,6 +622,73 @@ void _tests() {
|
||||
// button and the right edge of the 800 wide window.
|
||||
expect(tester.getBottomLeft(find.text('OK')).dx, 800 - ltrOkRight);
|
||||
});
|
||||
|
||||
testWidgets('uses root navigator by default', (WidgetTester tester) async {
|
||||
final PickerObserver rootObserver = PickerObserver();
|
||||
final PickerObserver nestedObserver = PickerObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showTimePicker(
|
||||
context: context,
|
||||
initialTime: const TimeOfDay(hour: 7, minute: 0),
|
||||
);
|
||||
},
|
||||
child: const Text('Show Picker'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.pickerCount, 1);
|
||||
expect(nestedObserver.pickerCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||
final PickerObserver rootObserver = PickerObserver();
|
||||
final PickerObserver nestedObserver = PickerObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showTimePicker(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
initialTime: const TimeOfDay(hour: 7, minute: 0),
|
||||
);
|
||||
},
|
||||
child: const Text('Show Picker'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.pickerCount, 0);
|
||||
expect(nestedObserver.pickerCount, 1);
|
||||
});
|
||||
}
|
||||
|
||||
final Finder findDialPaint = find.descendant(
|
||||
@@ -695,3 +762,15 @@ class _CustomPainterSemanticsTester {
|
||||
expect(tester.renderObject(findDialPaint), expectedLabels);
|
||||
}
|
||||
}
|
||||
|
||||
class PickerObserver extends NavigatorObserver {
|
||||
int pickerCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route.toString().contains('_DialogRoute')) {
|
||||
pickerCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -788,6 +788,81 @@ void main() {
|
||||
expect(secondaryAnimationPageOne.parent, kAlwaysDismissedAnimation);
|
||||
expect(trainHopper2.currentTrain, isNull); // Has been disposed.
|
||||
});
|
||||
|
||||
testWidgets('showGeneralDialog uses root navigator by default', (WidgetTester tester) async {
|
||||
final DialogObserver rootObserver = DialogObserver();
|
||||
final DialogObserver nestedObserver = DialogObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showGeneralDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
transitionDuration: Duration.zero,
|
||||
pageBuilder: (BuildContext innerContext, _, __) {
|
||||
return const SizedBox();
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('Show Dialog'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.dialogCount, 1);
|
||||
expect(nestedObserver.dialogCount, 0);
|
||||
});
|
||||
|
||||
testWidgets('showGeneralDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||
final DialogObserver rootObserver = DialogObserver();
|
||||
final DialogObserver nestedObserver = DialogObserver();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||
home: Navigator(
|
||||
observers: <NavigatorObserver>[nestedObserver],
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<dynamic>(
|
||||
builder: (BuildContext context) {
|
||||
return RaisedButton(
|
||||
onPressed: () {
|
||||
showGeneralDialog<void>(
|
||||
useRootNavigator: false,
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
transitionDuration: Duration.zero,
|
||||
pageBuilder: (BuildContext innerContext, _, __) {
|
||||
return const SizedBox();
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('Show Dialog'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
));
|
||||
|
||||
// Open the dialog.
|
||||
await tester.tap(find.byType(RaisedButton));
|
||||
|
||||
expect(rootObserver.dialogCount, 0);
|
||||
expect(nestedObserver.dialogCount, 1);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -805,3 +880,15 @@ class TestPageRouteBuilder extends PageRouteBuilder<void> {
|
||||
return CurvedAnimation(parent: super.createAnimation(), curve: Curves.easeOutExpo);
|
||||
}
|
||||
}
|
||||
|
||||
class DialogObserver extends NavigatorObserver {
|
||||
int dialogCount = 0;
|
||||
|
||||
@override
|
||||
void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
|
||||
if (route.toString().contains('_DialogRoute')) {
|
||||
dialogCount++;
|
||||
}
|
||||
super.didPush(route, previousRoute);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user