mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Add showCupertinoDialog and showGeneralDialog (#20152)
This commit is contained in:
parent
e770685a4b
commit
345d939ee8
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
@ -9,6 +11,9 @@ import 'package:flutter/widgets.dart';
|
||||
const double _kBackGestureWidth = 20.0;
|
||||
const double _kMinFlingVelocity = 1.0; // Screen widths per second.
|
||||
|
||||
// Barrier color for a Cupertino modal barrier.
|
||||
const Color _kModalBarrierColor = Color(0x6604040F);
|
||||
|
||||
// Offset from offscreen to the right to fully on screen.
|
||||
final Tween<Offset> _kRightMiddleTween = new Tween<Offset>(
|
||||
begin: const Offset(1.0, 0.0),
|
||||
@ -709,3 +714,77 @@ class _CupertinoEdgeShadowPainter extends BoxPainter {
|
||||
canvas.drawRect(rect, paint);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
|
||||
final CurvedAnimation fadeAnimation = new CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
if (animation.status == AnimationStatus.reverse) {
|
||||
return new FadeTransition(
|
||||
opacity: fadeAnimation,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
return new FadeTransition(
|
||||
opacity: fadeAnimation,
|
||||
child: ScaleTransition(
|
||||
child: child,
|
||||
scale: new Tween<double>(
|
||||
begin: 1.2,
|
||||
end: 1.0,
|
||||
).animate(
|
||||
new CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.fastOutSlowIn,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Displays an iOS-style dialog above the current contents of the app, with
|
||||
/// iOS-style entrance and exit animations, modal barrier color, and modal
|
||||
/// barrier behavior (the dialog is not dismissible with a tap on the barrier).
|
||||
///
|
||||
/// This function takes a `builder` which typically builds a [CupertinoDialog]
|
||||
/// or [CupertinoAlertDialog] widget. Content below the dialog is dimmed with a
|
||||
/// [ModalBarrier]. The widget returned by the `builder` does not share a
|
||||
/// context with the location that `showCupertinoDialog` is originally called
|
||||
/// from. Use a [StatefulBuilder] or a custom [StatefulWidget] if the dialog
|
||||
/// needs to update dynamically.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// 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:
|
||||
/// * [CupertinoDialog], an iOS-style dialog.
|
||||
/// * [CupertinoAlertDialog], an iOS-style alert dialog.
|
||||
/// * [showDialog], which displays a Material-style dialog.
|
||||
/// * [showGeneralDialog], which allows for customization of the dialog popup.
|
||||
/// * <https://developer.apple.com/ios/human-interface-guidelines/views/alerts/>
|
||||
Future<T> showCupertinoDialog<T>({
|
||||
@required BuildContext context,
|
||||
@required WidgetBuilder builder,
|
||||
}) {
|
||||
assert(builder != null);
|
||||
return showGeneralDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
barrierColor: _kModalBarrierColor,
|
||||
transitionDuration: const Duration(milliseconds: 300),
|
||||
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||
return builder(context);
|
||||
},
|
||||
transitionBuilder: _buildCupertinoDialogTransitions,
|
||||
);
|
||||
}
|
@ -543,70 +543,25 @@ class SimpleDialog extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class _DialogRoute<T> extends PopupRoute<T> {
|
||||
_DialogRoute({
|
||||
@required this.theme,
|
||||
bool barrierDismissible = true,
|
||||
this.barrierLabel,
|
||||
@required this.child,
|
||||
RouteSettings settings,
|
||||
}) : assert(barrierDismissible != null),
|
||||
_barrierDismissible = barrierDismissible,
|
||||
super(settings: settings);
|
||||
|
||||
final Widget child;
|
||||
final ThemeData theme;
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => const Duration(milliseconds: 150);
|
||||
|
||||
@override
|
||||
bool get barrierDismissible => _barrierDismissible;
|
||||
final bool _barrierDismissible;
|
||||
|
||||
@override
|
||||
Color get barrierColor => Colors.black54;
|
||||
|
||||
@override
|
||||
final String barrierLabel;
|
||||
|
||||
@override
|
||||
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||
return new SafeArea(
|
||||
child: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
final Widget annotatedChild = new Semantics(
|
||||
child: child,
|
||||
scopesRoute: true,
|
||||
explicitChildNodes: true,
|
||||
);
|
||||
return theme != null
|
||||
? new Theme(data: theme, child: annotatedChild)
|
||||
: annotatedChild;
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
|
||||
return new FadeTransition(
|
||||
opacity: new CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.easeOut
|
||||
),
|
||||
child: child
|
||||
);
|
||||
}
|
||||
Widget _buildMaterialDialogTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
|
||||
return new FadeTransition(
|
||||
opacity: new CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.easeOut,
|
||||
),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
/// Displays a dialog above the current contents of the app.
|
||||
/// Displays a Material dialog above the current contents of the app, with
|
||||
/// Material entrance and exit animations, modal barrier color, and modal
|
||||
/// barrier behavior (dialog is dismissible with a tap on the barrier).
|
||||
///
|
||||
/// This function takes a `builder` which typically builds a [Dialog] widget.
|
||||
/// Content below the dialog is dimmed with a [ModalBarrier]. This widget does
|
||||
/// not share a context with the location that `showDialog` is originally
|
||||
/// called from. Use a [StatefulBuilder] or a custom [StatefulWidget] if the
|
||||
/// dialog needs to update dynamically.
|
||||
/// Content below the dialog is dimmed with a [ModalBarrier]. The widget
|
||||
/// returned by the `builder` does not share a context with the location that
|
||||
/// `showDialog` is originally called from. Use a [StatefulBuilder] or a
|
||||
/// custom [StatefulWidget] if the dialog needs to update dynamically.
|
||||
///
|
||||
/// 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
|
||||
@ -620,13 +575,15 @@ class _DialogRoute<T> extends PopupRoute<T> {
|
||||
/// 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 just 'Navigator.pop(context, result)`.
|
||||
/// dialog rather than just `Navigator.pop(context, result)`.
|
||||
///
|
||||
/// See also:
|
||||
/// * [AlertDialog], for dialogs that have a row of buttons below a body.
|
||||
/// * [SimpleDialog], which handles the scrolling of the contents and does
|
||||
/// not show buttons below its body.
|
||||
/// * [Dialog], on which [SimpleDialog] and [AlertDialog] are based.
|
||||
/// * [showCupertinoDialog], which displays an iOS-style dialog.
|
||||
/// * [showGeneralDialog], which allows for customization of the dialog popup.
|
||||
/// * <https://material.google.com/components/dialogs.html>
|
||||
Future<T> showDialog<T>({
|
||||
@required BuildContext context,
|
||||
@ -639,10 +596,25 @@ Future<T> showDialog<T>({
|
||||
WidgetBuilder builder,
|
||||
}) {
|
||||
assert(child == null || builder == null);
|
||||
return Navigator.of(context, rootNavigator: true).push(new _DialogRoute<T>(
|
||||
child: child ?? new Builder(builder: builder),
|
||||
theme: Theme.of(context, shadowThemeOnly: true),
|
||||
return showGeneralDialog(
|
||||
context: context,
|
||||
pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||
final ThemeData theme = Theme.of(context, shadowThemeOnly: true);
|
||||
final Widget pageChild = child ?? new Builder(builder: builder);
|
||||
return new SafeArea(
|
||||
child: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
return theme != null
|
||||
? new Theme(data: theme, child: pageChild)
|
||||
: pageChild;
|
||||
}
|
||||
),
|
||||
);
|
||||
},
|
||||
barrierDismissible: barrierDismissible,
|
||||
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
|
||||
));
|
||||
}
|
||||
barrierColor: Colors.black54,
|
||||
transitionDuration: const Duration(milliseconds: 150),
|
||||
transitionBuilder: _buildMaterialDialogTransitions,
|
||||
);
|
||||
}
|
@ -44,18 +44,6 @@ abstract class PageRoute<T> extends ModalRoute<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Signature for the [PageRouteBuilder] function that builds the route's
|
||||
/// primary contents.
|
||||
///
|
||||
/// See [ModalRoute.buildPage] for complete definition of the parameters.
|
||||
typedef Widget RoutePageBuilder(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation);
|
||||
|
||||
/// Signature for the [PageRouteBuilder] function that builds the route's
|
||||
/// transitions.
|
||||
///
|
||||
/// See [ModalRoute.buildTransitions] for complete definition of the parameters.
|
||||
typedef Widget RouteTransitionsBuilder(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child);
|
||||
|
||||
Widget _defaultTransitionsBuilder(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
|
||||
return child;
|
||||
}
|
||||
|
@ -1379,3 +1379,143 @@ abstract class RouteAware {
|
||||
/// longer visible.
|
||||
void didPushNext() { }
|
||||
}
|
||||
|
||||
class _DialogRoute<T> extends PopupRoute<T> {
|
||||
_DialogRoute({
|
||||
@required RoutePageBuilder pageBuilder,
|
||||
bool barrierDismissible = true,
|
||||
String barrierLabel,
|
||||
Color barrierColor = const Color(0x80000000),
|
||||
Duration transitionDuration = const Duration(milliseconds: 200),
|
||||
RouteTransitionsBuilder transitionBuilder,
|
||||
RouteSettings settings,
|
||||
}) : assert(barrierDismissible != null),
|
||||
_pageBuilder = pageBuilder,
|
||||
_barrierDismissible = barrierDismissible,
|
||||
_barrierLabel = barrierLabel,
|
||||
_barrierColor = barrierColor,
|
||||
_transitionDuration = transitionDuration,
|
||||
_transitionBuilder = transitionBuilder,
|
||||
super(settings: settings);
|
||||
|
||||
final RoutePageBuilder _pageBuilder;
|
||||
|
||||
@override
|
||||
bool get barrierDismissible => _barrierDismissible;
|
||||
final bool _barrierDismissible;
|
||||
|
||||
@override
|
||||
String get barrierLabel => _barrierLabel;
|
||||
final String _barrierLabel;
|
||||
|
||||
@override
|
||||
Color get barrierColor => _barrierColor;
|
||||
final Color _barrierColor;
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => _transitionDuration;
|
||||
final Duration _transitionDuration;
|
||||
|
||||
final RouteTransitionsBuilder _transitionBuilder;
|
||||
|
||||
@override
|
||||
Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||
return new Semantics(
|
||||
child: _pageBuilder(context, animation, secondaryAnimation),
|
||||
scopesRoute: true,
|
||||
explicitChildNodes: true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
|
||||
if (_transitionBuilder == null) {
|
||||
return new FadeTransition(
|
||||
opacity: new CurvedAnimation(
|
||||
parent: animation,
|
||||
curve: Curves.linear,
|
||||
),
|
||||
child: child);
|
||||
} // Some default transition
|
||||
return _transitionBuilder(context, animation, secondaryAnimation, child);
|
||||
}
|
||||
}
|
||||
|
||||
/// Displays a dialog above the current contents of the app.
|
||||
///
|
||||
/// This function allows for customization of aspects of the dialog popup.
|
||||
///
|
||||
/// This function takes a `pageBuilder` which is used to build the primary
|
||||
/// content of the route (typically a dialog widget). Content below the dialog
|
||||
/// is dimmed with a [ModalBarrier]. The widget returned by the `pageBuilder`
|
||||
/// does not share a context with the location that `showGeneralDialog` is
|
||||
/// originally called from. Use a [StatefulBuilder] or a custom
|
||||
/// [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 `barrierDismissible` argument is used to determine whether this route
|
||||
/// can be dismissed by tapping the modal barrier. This argument defaults
|
||||
/// to true. If `barrierDismissible` is true, a non-null `barrierLabel` must be
|
||||
/// provided.
|
||||
///
|
||||
/// The `barrierLabel` argument is the semantic label used for a dismissible
|
||||
/// barrier. This argument defaults to "Dismiss".
|
||||
///
|
||||
/// The `barrierColor` argument is the color used for the modal barrier. This
|
||||
/// argument defaults to `Color(0x80000000)`.
|
||||
///
|
||||
/// The `transitionDuration` argument is used to determine how long it takes
|
||||
/// for the route to arrive on or leave off the screen. This argument defaults
|
||||
/// to 200 milliseconds.
|
||||
///
|
||||
/// The `transitionBuilder` argument is used to define how the route arrives on
|
||||
/// and leaves off the screen. By default, the transition is a linear fade of
|
||||
/// the page's contents.
|
||||
///
|
||||
/// 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.
|
||||
/// * [showCupertinoDialog], which displays an iOS-style dialog.
|
||||
Future<T> showGeneralDialog<T>({
|
||||
@required BuildContext context,
|
||||
@required RoutePageBuilder pageBuilder,
|
||||
bool barrierDismissible,
|
||||
String barrierLabel,
|
||||
Color barrierColor,
|
||||
Duration transitionDuration,
|
||||
RouteTransitionsBuilder transitionBuilder,
|
||||
}) {
|
||||
assert(pageBuilder != null);
|
||||
assert(!barrierDismissible || barrierLabel != null);
|
||||
return Navigator.of(context, rootNavigator: true).push(new _DialogRoute<T>(
|
||||
pageBuilder: pageBuilder,
|
||||
barrierDismissible: barrierDismissible,
|
||||
barrierLabel: barrierLabel,
|
||||
barrierColor: barrierColor,
|
||||
transitionDuration: transitionDuration,
|
||||
transitionBuilder: transitionBuilder,
|
||||
));
|
||||
}
|
||||
|
||||
/// Signature for the function that builds a route's primary contents.
|
||||
/// Used in [PageRouteBuilder] and [showGeneralDialog].
|
||||
///
|
||||
/// See [ModalRoute.buildPage] for complete definition of the parameters.
|
||||
typedef Widget RoutePageBuilder(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation);
|
||||
|
||||
/// Signature for the function that builds a route's transitions.
|
||||
/// Used in [PageRouteBuilder] and [showGeneralDialog].
|
||||
///
|
||||
/// See [ModalRoute.buildTransitions] for complete definition of the parameters.
|
||||
typedef Widget RouteTransitionsBuilder(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child);
|
||||
|
@ -332,6 +332,176 @@ void main() {
|
||||
expect(scrollController.offset, 0.0);
|
||||
expect(find.widgetWithText(CupertinoDialogAction, 'One'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('ScaleTransition animation for showCupertinoDialog()', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new CupertinoApp(
|
||||
home: new Center(
|
||||
child: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
return new CupertinoButton(
|
||||
onPressed: () {
|
||||
showCupertinoDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return new CupertinoAlertDialog(
|
||||
title: const Text('The title'),
|
||||
content: const Text('The content'),
|
||||
actions: <Widget>[
|
||||
const CupertinoDialogAction(
|
||||
child: Text('Cancel'),
|
||||
),
|
||||
new CupertinoDialogAction(
|
||||
isDestructiveAction: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('Go'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Go'));
|
||||
|
||||
// Enter animation.
|
||||
await tester.pump();
|
||||
Transform transform = tester.widget(find.byType(Transform));
|
||||
expect(transform.transform[0], closeTo(1.2, 0.01));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
transform = tester.widget(find.byType(Transform));
|
||||
expect(transform.transform[0], closeTo(1.182, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
transform = tester.widget(find.byType(Transform));
|
||||
expect(transform.transform[0], closeTo(1.108, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
transform = tester.widget(find.byType(Transform));
|
||||
expect(transform.transform[0], closeTo(1.044, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
transform = tester.widget(find.byType(Transform));
|
||||
expect(transform.transform[0], closeTo(1.015, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
transform = tester.widget(find.byType(Transform));
|
||||
expect(transform.transform[0], closeTo(1.003, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
transform = tester.widget(find.byType(Transform));
|
||||
expect(transform.transform[0], closeTo(1.000, 0.001));
|
||||
|
||||
await tester.tap(find.text('Delete'));
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
|
||||
// No scaling on exit animation.
|
||||
expect(find.byType(Transform), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('FadeTransition animation for showCupertinoDialog()', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new CupertinoApp(
|
||||
home: new Center(
|
||||
child: new Builder(
|
||||
builder: (BuildContext context) {
|
||||
return new CupertinoButton(
|
||||
onPressed: () {
|
||||
showCupertinoDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return new CupertinoAlertDialog(
|
||||
title: const Text('The title'),
|
||||
content: const Text('The content'),
|
||||
actions: <Widget>[
|
||||
const CupertinoDialogAction(
|
||||
child: Text('Cancel'),
|
||||
),
|
||||
new CupertinoDialogAction(
|
||||
isDestructiveAction: true,
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text('Delete'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: const Text('Go'),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Go'));
|
||||
|
||||
// Enter animation.
|
||||
await tester.pump();
|
||||
FadeTransition transition = tester.firstWidget(find.byType(FadeTransition));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.firstWidget(find.byType(FadeTransition));
|
||||
expect(transition.opacity.value, closeTo(0.10, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.firstWidget(find.byType(FadeTransition));
|
||||
expect(transition.opacity.value, closeTo(0.156, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.firstWidget(find.byType(FadeTransition));
|
||||
expect(transition.opacity.value, closeTo(0.324, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.firstWidget(find.byType(FadeTransition));
|
||||
expect(transition.opacity.value, closeTo(0.606, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.firstWidget(find.byType(FadeTransition));
|
||||
expect(transition.opacity.value, closeTo(1.0, 0.001));
|
||||
|
||||
await tester.tap(find.text('Delete'));
|
||||
|
||||
// Exit animation, look at reverse FadeTransition.
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1);
|
||||
expect(transition.opacity.value, closeTo(0.358, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1);
|
||||
expect(transition.opacity.value, closeTo(0.231, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1);
|
||||
expect(transition.opacity.value, closeTo(0.128, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1);
|
||||
expect(transition.opacity.value, closeTo(0.056, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1);
|
||||
expect(transition.opacity.value, closeTo(0.013, 0.001));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 25));
|
||||
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1);
|
||||
expect(transition.opacity.value, closeTo(0.0, 0.001));
|
||||
});
|
||||
}
|
||||
|
||||
Widget boilerplate(Widget child) {
|
||||
|
Loading…
Reference in New Issue
Block a user