mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Support custom transition duration for DialogRoute
, CupertinoDialogRoute
and show dialog methods. (#154048)
Currently we don't support custom transition duration for `DialogRoute`, `CupertinoDialogRoute` and show dialog methods , This PR will to support that.
This commit is contained in:
parent
d9321159bf
commit
0eaeb0d1c5
@ -57,6 +57,9 @@ const Color kCupertinoModalBarrierColor = CupertinoDynamicColor.withBrightness(
|
|||||||
// The duration of the transition used when a modal popup is shown.
|
// The duration of the transition used when a modal popup is shown.
|
||||||
const Duration _kModalPopupTransitionDuration = Duration(milliseconds: 335);
|
const Duration _kModalPopupTransitionDuration = Duration(milliseconds: 335);
|
||||||
|
|
||||||
|
// The transition duration used for [CupertinoDialogRoute] transitions.
|
||||||
|
const Duration _kCupertinoDialogRouteTransitionDuration = Duration(milliseconds: 250);
|
||||||
|
|
||||||
// Offset from offscreen to the right to fully on screen.
|
// Offset from offscreen to the right to fully on screen.
|
||||||
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
|
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
|
||||||
begin: const Offset(1.0, 0.0),
|
begin: const Offset(1.0, 0.0),
|
||||||
@ -1269,6 +1272,10 @@ Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double>
|
|||||||
/// By default, `useRootNavigator` is `true` and the dialog route created by
|
/// By default, `useRootNavigator` is `true` and the dialog route created by
|
||||||
/// this method is pushed to the root navigator.
|
/// this method is pushed to the root navigator.
|
||||||
///
|
///
|
||||||
|
/// the `transitionDuration` argument is used to specify the duration of
|
||||||
|
/// the dialog's entrance and exit animations. If it's not provided or `null`,
|
||||||
|
/// then it uses the default value as set by [CupertinoDialogRoute].
|
||||||
|
///
|
||||||
/// {@macro flutter.widgets.RawDialogRoute}
|
/// {@macro flutter.widgets.RawDialogRoute}
|
||||||
///
|
///
|
||||||
/// If the application has multiple [Navigator] objects, it may be necessary to
|
/// If the application has multiple [Navigator] objects, it may be necessary to
|
||||||
@ -1313,6 +1320,7 @@ Future<T?> showCupertinoDialog<T>({
|
|||||||
bool barrierDismissible = false,
|
bool barrierDismissible = false,
|
||||||
RouteSettings? routeSettings,
|
RouteSettings? routeSettings,
|
||||||
Offset? anchorPoint,
|
Offset? anchorPoint,
|
||||||
|
Duration? transitionDuration,
|
||||||
}) {
|
}) {
|
||||||
|
|
||||||
return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(CupertinoDialogRoute<T>(
|
return Navigator.of(context, rootNavigator: useRootNavigator).push<T>(CupertinoDialogRoute<T>(
|
||||||
@ -1323,6 +1331,7 @@ Future<T?> showCupertinoDialog<T>({
|
|||||||
barrierColor: CupertinoDynamicColor.resolve(kCupertinoModalBarrierColor, context),
|
barrierColor: CupertinoDynamicColor.resolve(kCupertinoModalBarrierColor, context),
|
||||||
settings: routeSettings,
|
settings: routeSettings,
|
||||||
anchorPoint: anchorPoint,
|
anchorPoint: anchorPoint,
|
||||||
|
transitionDuration: transitionDuration,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1350,6 +1359,10 @@ Future<T?> showCupertinoDialog<T>({
|
|||||||
/// barrier that darkens everything below the dialog. If `null`, then
|
/// barrier that darkens everything below the dialog. If `null`, then
|
||||||
/// [CupertinoDynamicColor.resolve] is used to compute the modal color.
|
/// [CupertinoDynamicColor.resolve] is used to compute the modal color.
|
||||||
///
|
///
|
||||||
|
/// The `transitionDuration` argument is used to specify the duration of
|
||||||
|
/// the dialog's entrance and exit animations. If it's not provided or `null`,
|
||||||
|
/// then the default duration `Duration(milliseconds: 250)` is used.
|
||||||
|
///
|
||||||
/// The `settings` argument define the settings for this route. See
|
/// The `settings` argument define the settings for this route. See
|
||||||
/// [RouteSettings] for details.
|
/// [RouteSettings] for details.
|
||||||
///
|
///
|
||||||
@ -1372,7 +1385,7 @@ class CupertinoDialogRoute<T> extends RawDialogRoute<T> {
|
|||||||
Color? barrierColor,
|
Color? barrierColor,
|
||||||
String? barrierLabel,
|
String? barrierLabel,
|
||||||
// This transition duration was eyeballed comparing with iOS
|
// This transition duration was eyeballed comparing with iOS
|
||||||
super.transitionDuration = const Duration(milliseconds: 250),
|
Duration? transitionDuration,
|
||||||
this.transitionBuilder,
|
this.transitionBuilder,
|
||||||
super.settings,
|
super.settings,
|
||||||
super.requestFocus,
|
super.requestFocus,
|
||||||
@ -1384,6 +1397,7 @@ class CupertinoDialogRoute<T> extends RawDialogRoute<T> {
|
|||||||
transitionBuilder: transitionBuilder ?? _buildCupertinoDialogTransitions,
|
transitionBuilder: transitionBuilder ?? _buildCupertinoDialogTransitions,
|
||||||
barrierLabel: barrierLabel ?? CupertinoLocalizations.of(context).modalBarrierDismissLabel,
|
barrierLabel: barrierLabel ?? CupertinoLocalizations.of(context).modalBarrierDismissLabel,
|
||||||
barrierColor: barrierColor ?? CupertinoDynamicColor.resolve(kCupertinoModalBarrierColor, context),
|
barrierColor: barrierColor ?? CupertinoDynamicColor.resolve(kCupertinoModalBarrierColor, context),
|
||||||
|
transitionDuration: transitionDuration ?? _kCupertinoDialogRouteTransitionDuration,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// Custom transition builder
|
/// Custom transition builder
|
||||||
|
@ -31,6 +31,9 @@ import 'theme_data.dart';
|
|||||||
|
|
||||||
const EdgeInsets _defaultInsetPadding = EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0);
|
const EdgeInsets _defaultInsetPadding = EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0);
|
||||||
|
|
||||||
|
// The transition duration used for [DialogRoute] transitions.
|
||||||
|
const Duration _kDialogRouteTransitionDuration = Duration(milliseconds: 150);
|
||||||
|
|
||||||
/// A Material Design dialog.
|
/// A Material Design dialog.
|
||||||
///
|
///
|
||||||
/// This dialog widget does not have any opinion about the contents of the
|
/// This dialog widget does not have any opinion about the contents of the
|
||||||
@ -1345,6 +1348,10 @@ Widget _buildMaterialDialogTransitions(BuildContext context, Animation<double> a
|
|||||||
/// field from `DialogThemeData` is used. If that is `null` the default color
|
/// field from `DialogThemeData` is used. If that is `null` the default color
|
||||||
/// `Colors.black54` is used.
|
/// `Colors.black54` is used.
|
||||||
///
|
///
|
||||||
|
/// the `transitionDuration` argument is used to specify the duration of
|
||||||
|
/// the dialog's entrance and exit animations. If it's not provided or `null`,
|
||||||
|
/// then it uses the default value as set by [DialogRoute].
|
||||||
|
///
|
||||||
/// The `useSafeArea` argument is used to indicate if the dialog should only
|
/// The `useSafeArea` argument is used to indicate if the dialog should only
|
||||||
/// display in 'safe' areas of the screen not used by the operating system
|
/// display in 'safe' areas of the screen not used by the operating system
|
||||||
/// (see [SafeArea] for more details). It is `true` by default, which means
|
/// (see [SafeArea] for more details). It is `true` by default, which means
|
||||||
@ -1428,6 +1435,7 @@ Future<T?> showDialog<T>({
|
|||||||
RouteSettings? routeSettings,
|
RouteSettings? routeSettings,
|
||||||
Offset? anchorPoint,
|
Offset? anchorPoint,
|
||||||
TraversalEdgeBehavior? traversalEdgeBehavior,
|
TraversalEdgeBehavior? traversalEdgeBehavior,
|
||||||
|
Duration? transitionDuration,
|
||||||
}) {
|
}) {
|
||||||
assert(_debugIsActive(context));
|
assert(_debugIsActive(context));
|
||||||
assert(debugCheckHasMaterialLocalizations(context));
|
assert(debugCheckHasMaterialLocalizations(context));
|
||||||
@ -1454,6 +1462,7 @@ Future<T?> showDialog<T>({
|
|||||||
themes: themes,
|
themes: themes,
|
||||||
anchorPoint: anchorPoint,
|
anchorPoint: anchorPoint,
|
||||||
traversalEdgeBehavior: traversalEdgeBehavior ?? TraversalEdgeBehavior.closedLoop,
|
traversalEdgeBehavior: traversalEdgeBehavior ?? TraversalEdgeBehavior.closedLoop,
|
||||||
|
transitionDuration: transitionDuration,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1465,6 +1474,10 @@ Future<T?> showDialog<T>({
|
|||||||
///
|
///
|
||||||
/// On Cupertino platforms, [barrierColor], [useSafeArea], and
|
/// On Cupertino platforms, [barrierColor], [useSafeArea], and
|
||||||
/// [traversalEdgeBehavior] are ignored.
|
/// [traversalEdgeBehavior] are ignored.
|
||||||
|
///
|
||||||
|
/// The `transitionDuration` argument is used to specify the duration of
|
||||||
|
/// the dialog's entrance and exit animations. If it's not provided or `null`,
|
||||||
|
/// then it uses the default value as set by [DialogRoute] or [CupertinoDialogRoute].
|
||||||
Future<T?> showAdaptiveDialog<T>({
|
Future<T?> showAdaptiveDialog<T>({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required WidgetBuilder builder,
|
required WidgetBuilder builder,
|
||||||
@ -1476,6 +1489,7 @@ Future<T?> showAdaptiveDialog<T>({
|
|||||||
RouteSettings? routeSettings,
|
RouteSettings? routeSettings,
|
||||||
Offset? anchorPoint,
|
Offset? anchorPoint,
|
||||||
TraversalEdgeBehavior? traversalEdgeBehavior,
|
TraversalEdgeBehavior? traversalEdgeBehavior,
|
||||||
|
Duration? transitionDuration,
|
||||||
}) {
|
}) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
switch (theme.platform) {
|
switch (theme.platform) {
|
||||||
@ -1494,6 +1508,7 @@ Future<T?> showAdaptiveDialog<T>({
|
|||||||
routeSettings: routeSettings,
|
routeSettings: routeSettings,
|
||||||
anchorPoint: anchorPoint,
|
anchorPoint: anchorPoint,
|
||||||
traversalEdgeBehavior: traversalEdgeBehavior,
|
traversalEdgeBehavior: traversalEdgeBehavior,
|
||||||
|
transitionDuration: transitionDuration,
|
||||||
);
|
);
|
||||||
case TargetPlatform.iOS:
|
case TargetPlatform.iOS:
|
||||||
case TargetPlatform.macOS:
|
case TargetPlatform.macOS:
|
||||||
@ -1505,6 +1520,7 @@ Future<T?> showAdaptiveDialog<T>({
|
|||||||
useRootNavigator: useRootNavigator,
|
useRootNavigator: useRootNavigator,
|
||||||
anchorPoint: anchorPoint,
|
anchorPoint: anchorPoint,
|
||||||
routeSettings: routeSettings,
|
routeSettings: routeSettings,
|
||||||
|
transitionDuration: transitionDuration,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1552,6 +1568,10 @@ bool _debugIsActive(BuildContext context) {
|
|||||||
/// barrier that darkens everything below the dialog. If `null`, the default
|
/// barrier that darkens everything below the dialog. If `null`, the default
|
||||||
/// color `Colors.black54` is used.
|
/// color `Colors.black54` is used.
|
||||||
///
|
///
|
||||||
|
/// the `transitionDuration` argument is used to specify the duration of
|
||||||
|
/// the dialog's entrance and exit animations. If it's not provided or `null`,
|
||||||
|
/// then the default duration `Duration(milliseconds: 150)` is used.
|
||||||
|
///
|
||||||
/// The `useSafeArea` argument is used to indicate if the dialog should only
|
/// The `useSafeArea` argument is used to indicate if the dialog should only
|
||||||
/// display in 'safe' areas of the screen not used by the operating system
|
/// display in 'safe' areas of the screen not used by the operating system
|
||||||
/// (see [SafeArea] for more details). It is `true` by default, which means
|
/// (see [SafeArea] for more details). It is `true` by default, which means
|
||||||
@ -1582,6 +1602,7 @@ class DialogRoute<T> extends RawDialogRoute<T> {
|
|||||||
super.barrierDismissible,
|
super.barrierDismissible,
|
||||||
String? barrierLabel,
|
String? barrierLabel,
|
||||||
bool useSafeArea = true,
|
bool useSafeArea = true,
|
||||||
|
Duration? transitionDuration,
|
||||||
super.settings,
|
super.settings,
|
||||||
super.requestFocus,
|
super.requestFocus,
|
||||||
super.anchorPoint,
|
super.anchorPoint,
|
||||||
@ -1596,8 +1617,8 @@ class DialogRoute<T> extends RawDialogRoute<T> {
|
|||||||
return dialog;
|
return dialog;
|
||||||
},
|
},
|
||||||
barrierLabel: barrierLabel ?? MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
barrierLabel: barrierLabel ?? MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
||||||
transitionDuration: const Duration(milliseconds: 150),
|
|
||||||
transitionBuilder: _buildMaterialDialogTransitions,
|
transitionBuilder: _buildMaterialDialogTransitions,
|
||||||
|
transitionDuration: transitionDuration ?? _kDialogRouteTransitionDuration,
|
||||||
);
|
);
|
||||||
|
|
||||||
CurvedAnimation? _curvedAnimation;
|
CurvedAnimation? _curvedAnimation;
|
||||||
|
@ -1883,6 +1883,45 @@ void main() {
|
|||||||
expect(nestedObserver.dialogCount, 0);
|
expect(nestedObserver.dialogCount, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('showCupertinoDialog - custom transitionDuration', (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,
|
||||||
|
transitionDuration: const Duration(milliseconds: 50),
|
||||||
|
builder: (BuildContext context) => const SizedBox(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('tap'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
// Open the dialog.
|
||||||
|
await tester.tap(find.text('tap'));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(rootObserver.dialogCount, 1);
|
||||||
|
expect(nestedObserver.dialogCount, 0);
|
||||||
|
expect(rootObserver.dialogRoutes.length, equals(1));
|
||||||
|
final ModalRoute<dynamic> route = rootObserver.dialogRoutes.last;
|
||||||
|
expect(route is CupertinoDialogRoute, true);
|
||||||
|
expect(route.transitionDuration.inMilliseconds, 50);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('showCupertinoDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
testWidgets('showCupertinoDialog uses nested navigator if useRootNavigator is false', (WidgetTester tester) async {
|
||||||
final DialogObserver rootObserver = DialogObserver();
|
final DialogObserver rootObserver = DialogObserver();
|
||||||
final DialogObserver nestedObserver = DialogObserver();
|
final DialogObserver nestedObserver = DialogObserver();
|
||||||
@ -2932,15 +2971,24 @@ class PopupObserver extends NavigatorObserver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DialogObserver extends NavigatorObserver {
|
class DialogObserver extends NavigatorObserver {
|
||||||
int dialogCount = 0;
|
final List<ModalRoute<dynamic>> dialogRoutes = <ModalRoute<dynamic>>[];
|
||||||
|
int get dialogCount => dialogRoutes.length;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
||||||
if (route is CupertinoDialogRoute) {
|
if (route is CupertinoDialogRoute) {
|
||||||
dialogCount++;
|
dialogRoutes.add(route);
|
||||||
}
|
}
|
||||||
super.didPush(route, previousRoute);
|
super.didPush(route, previousRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
||||||
|
if (route is CupertinoDialogRoute) {
|
||||||
|
dialogRoutes.removeLast();
|
||||||
|
}
|
||||||
|
super.didPop(route, previousRoute);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RouteSettingsObserver extends NavigatorObserver {
|
class RouteSettingsObserver extends NavigatorObserver {
|
||||||
|
@ -2533,6 +2533,50 @@ void main() {
|
|||||||
expect(currentRouteSetting.name, '/');
|
expect(currentRouteSetting.name, '/');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('showDialog - custom transitionDuration', (WidgetTester tester) async {
|
||||||
|
final DialogObserver rootObserver = DialogObserver();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||||
|
home: Material(
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: const Text('X'),
|
||||||
|
onPressed: () {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
transitionDuration: const Duration(milliseconds: 50),
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const AlertDialog(
|
||||||
|
title: Text('Title'),
|
||||||
|
content: Text('Y'),
|
||||||
|
actions: <Widget>[],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.text('X'));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(rootObserver.dialogRoutes.length, equals(1));
|
||||||
|
final ModalRoute<dynamic> route = rootObserver.dialogRoutes.last;
|
||||||
|
expect(route is DialogRoute, true);
|
||||||
|
expect(route.barrierDismissible, isNotNull);
|
||||||
|
expect(route.barrierColor, isNotNull);
|
||||||
|
expect(route.transitionDuration, isNotNull);
|
||||||
|
expect(route.transitionDuration.inMilliseconds, 50);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('showDialog - custom barrierLabel', (WidgetTester tester) async {
|
testWidgets('showDialog - custom barrierLabel', (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = SemanticsTester(tester);
|
final SemanticsTester semantics = SemanticsTester(tester);
|
||||||
|
|
||||||
@ -2829,6 +2873,50 @@ void main() {
|
|||||||
expect(find.text('Dialog2'), findsOneWidget);
|
expect(find.text('Dialog2'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('showAdaptiveDialog - custom transitionDuration', (WidgetTester tester) async {
|
||||||
|
final DialogObserver rootObserver = DialogObserver();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||||
|
home: Material(
|
||||||
|
child: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: ElevatedButton(
|
||||||
|
child: const Text('X'),
|
||||||
|
onPressed: () {
|
||||||
|
showAdaptiveDialog<void>(
|
||||||
|
context: context,
|
||||||
|
transitionDuration: const Duration(milliseconds: 50),
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const AlertDialog(
|
||||||
|
title: Text('Title'),
|
||||||
|
content: Text('Y'),
|
||||||
|
actions: <Widget>[],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.tap(find.text('X'));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(rootObserver.dialogRoutes.length, equals(1));
|
||||||
|
final ModalRoute<dynamic> route = rootObserver.dialogRoutes.last;
|
||||||
|
expect(route is DialogRoute, true);
|
||||||
|
expect(route.barrierDismissible, isNotNull);
|
||||||
|
expect(route.barrierColor, isNotNull);
|
||||||
|
expect(route.transitionDuration, isNotNull);
|
||||||
|
expect(route.transitionDuration.inMilliseconds, 50);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('Uses open focus traversal when overridden', (WidgetTester tester) async {
|
testWidgets('Uses open focus traversal when overridden', (WidgetTester tester) async {
|
||||||
final FocusNode okNode = FocusNode();
|
final FocusNode okNode = FocusNode();
|
||||||
addTearDown(okNode.dispose);
|
addTearDown(okNode.dispose);
|
||||||
@ -3002,15 +3090,24 @@ class _RestorableDialogTestWidget extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DialogObserver extends NavigatorObserver {
|
class DialogObserver extends NavigatorObserver {
|
||||||
int dialogCount = 0;
|
final List<ModalRoute<dynamic>> dialogRoutes = <ModalRoute<dynamic>>[];
|
||||||
|
int get dialogCount => dialogRoutes.length;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
||||||
if (route is DialogRoute) {
|
if (route is DialogRoute) {
|
||||||
dialogCount++;
|
dialogRoutes.add(route);
|
||||||
}
|
}
|
||||||
super.didPush(route, previousRoute);
|
super.didPush(route, previousRoute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
||||||
|
if (route is DialogRoute) {
|
||||||
|
dialogRoutes.removeLast();
|
||||||
|
}
|
||||||
|
super.didPop(route, previousRoute);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ClosureNavigatorObserver extends NavigatorObserver {
|
class _ClosureNavigatorObserver extends NavigatorObserver {
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@ -1920,6 +1921,233 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('DialogRoute', () {
|
||||||
|
testWidgets('DialogRoute - default transitionDuration', (WidgetTester tester) async {
|
||||||
|
final GlobalKey containerKey = GlobalKey();
|
||||||
|
final DialogObserver rootObserver = DialogObserver();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||||
|
home: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext innerContext) {
|
||||||
|
return Container(
|
||||||
|
key: containerKey,
|
||||||
|
color: Colors.green,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Open dialog'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the dialog.
|
||||||
|
await tester.tap(find.byType(ElevatedButton));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
expect(rootObserver.dialogCount, 1);
|
||||||
|
final ModalRoute<dynamic> route = rootObserver.dialogRoutes.last;
|
||||||
|
expect(route is RawDialogRoute, true);
|
||||||
|
expect(route.transitionDuration.inMilliseconds, 150);
|
||||||
|
|
||||||
|
// Pop the new route.
|
||||||
|
tester.state<NavigatorState>(find.byType(Navigator)).pop();
|
||||||
|
await tester.pump();
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container should be present halfway through the transition.
|
||||||
|
await tester.pump(const Duration(milliseconds: 75));
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container should be present at the very end of the transition.
|
||||||
|
await tester.pump(const Duration(milliseconds: 75));
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container have transitioned out after 150ms.
|
||||||
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
|
expect(find.byKey(containerKey), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('DialogRoute - custom transitionDuration', (WidgetTester tester) async {
|
||||||
|
final GlobalKey containerKey = GlobalKey();
|
||||||
|
final DialogObserver rootObserver = DialogObserver();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||||
|
home: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
transitionDuration: const Duration(milliseconds: 300),
|
||||||
|
builder: (BuildContext innerContext) {
|
||||||
|
return Container(
|
||||||
|
key: containerKey,
|
||||||
|
color: Colors.green,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Open dialog'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the dialog.
|
||||||
|
await tester.tap(find.byType(ElevatedButton));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
expect(rootObserver.dialogCount, 1);
|
||||||
|
final ModalRoute<dynamic> route = rootObserver.dialogRoutes.last;
|
||||||
|
expect(route is RawDialogRoute, true);
|
||||||
|
expect(route.transitionDuration.inMilliseconds, 300);
|
||||||
|
|
||||||
|
// Pop the new route.
|
||||||
|
tester.state<NavigatorState>(find.byType(Navigator)).pop();
|
||||||
|
await tester.pump();
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container should be present halfway through the transition.
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container should be present at the very end of the transition.
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container have transitioned out after 300ms.
|
||||||
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
|
expect(find.byKey(containerKey), findsNothing);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('CupertinoDialogRoute', () {
|
||||||
|
testWidgets('CupertinoDialogRoute - default transitionDuration', (WidgetTester tester) async {
|
||||||
|
final GlobalKey containerKey = GlobalKey();
|
||||||
|
final DialogObserver rootObserver = DialogObserver();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||||
|
theme: ThemeData(platform: TargetPlatform.iOS),
|
||||||
|
home: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
showCupertinoDialog<void>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext innerContext) {
|
||||||
|
return Container(
|
||||||
|
key: containerKey,
|
||||||
|
color: Colors.green,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Open dialog'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the dialog.
|
||||||
|
await tester.tap(find.byType(ElevatedButton));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
expect(rootObserver.dialogCount, 1);
|
||||||
|
final ModalRoute<dynamic> route = rootObserver.dialogRoutes.last;
|
||||||
|
expect(route is RawDialogRoute, true);
|
||||||
|
expect(route.transitionDuration.inMilliseconds, 250);
|
||||||
|
|
||||||
|
// Pop the new route.
|
||||||
|
tester.state<NavigatorState>(find.byType(Navigator)).pop();
|
||||||
|
await tester.pump();
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container should be present halfway through the transition.
|
||||||
|
await tester.pump(const Duration(milliseconds: 125));
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container should be present at the very end of the transition.
|
||||||
|
await tester.pump(const Duration(milliseconds: 125));
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container have transitioned out after 250ms.
|
||||||
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
|
expect(find.byKey(containerKey), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('CupertinoDialogRoute - custom transitionDuration', (WidgetTester tester) async {
|
||||||
|
final GlobalKey containerKey = GlobalKey();
|
||||||
|
final DialogObserver rootObserver = DialogObserver();
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
navigatorObservers: <NavigatorObserver>[rootObserver],
|
||||||
|
home: Builder(
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog<void>(
|
||||||
|
context: context,
|
||||||
|
transitionDuration: const Duration(milliseconds: 100),
|
||||||
|
builder: (BuildContext innerContext) {
|
||||||
|
return Container(
|
||||||
|
key: containerKey,
|
||||||
|
color: Colors.green,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Text('Open dialog'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Open the dialog.
|
||||||
|
await tester.tap(find.byType(ElevatedButton));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
expect(rootObserver.dialogCount, 1);
|
||||||
|
final ModalRoute<dynamic> route = rootObserver.dialogRoutes.last;
|
||||||
|
expect(route is RawDialogRoute, true);
|
||||||
|
expect(route.transitionDuration.inMilliseconds, 100);
|
||||||
|
|
||||||
|
// Pop the new route.
|
||||||
|
tester.state<NavigatorState>(find.byType(Navigator)).pop();
|
||||||
|
await tester.pump();
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container should be present halfway through the transition.
|
||||||
|
await tester.pump(const Duration(milliseconds: 50));
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container should be present at the very end of the transition.
|
||||||
|
await tester.pump(const Duration(milliseconds: 50));
|
||||||
|
expect(find.byKey(containerKey), findsOneWidget);
|
||||||
|
|
||||||
|
// Container have transitioned out after 100ms.
|
||||||
|
await tester.pump(const Duration(milliseconds: 1));
|
||||||
|
expect(find.byKey(containerKey), findsNothing);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('can be dismissed with escape keyboard shortcut', (WidgetTester tester) async {
|
testWidgets('can be dismissed with escape keyboard shortcut', (WidgetTester tester) async {
|
||||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
await tester.pumpWidget(MaterialApp(
|
await tester.pumpWidget(MaterialApp(
|
||||||
@ -2155,13 +2383,12 @@ class TestPageRouteBuilder extends PageRouteBuilder<void> {
|
|||||||
|
|
||||||
class DialogObserver extends NavigatorObserver {
|
class DialogObserver extends NavigatorObserver {
|
||||||
final List<ModalRoute<dynamic>> dialogRoutes = <ModalRoute<dynamic>>[];
|
final List<ModalRoute<dynamic>> dialogRoutes = <ModalRoute<dynamic>>[];
|
||||||
int dialogCount = 0;
|
int get dialogCount => dialogRoutes.length;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
||||||
if (route is RawDialogRoute) {
|
if (route is RawDialogRoute) {
|
||||||
dialogRoutes.add(route);
|
dialogRoutes.add(route);
|
||||||
dialogCount++;
|
|
||||||
}
|
}
|
||||||
super.didPush(route, previousRoute);
|
super.didPush(route, previousRoute);
|
||||||
}
|
}
|
||||||
@ -2170,7 +2397,6 @@ class DialogObserver extends NavigatorObserver {
|
|||||||
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
|
||||||
if (route is RawDialogRoute) {
|
if (route is RawDialogRoute) {
|
||||||
dialogRoutes.removeLast();
|
dialogRoutes.removeLast();
|
||||||
dialogCount--;
|
|
||||||
}
|
}
|
||||||
super.didPop(route, previousRoute);
|
super.didPop(route, previousRoute);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user