Commit 4418ec46 authored by Marco Scannadinari's avatar Marco Scannadinari Committed by xster

Adjust remaining Cupertino route animations to match native (#28597)

parent e525e642
......@@ -417,9 +417,13 @@ class CupertinoFullscreenDialogTransition extends StatelessWidget {
Key key,
@required Animation<double> animation,
@required this.child,
}) : _positionAnimation = animation
.drive(CurveTween(curve: Curves.easeInOut))
.drive(_kBottomUpTween),
}) : _positionAnimation = CurvedAnimation(
parent: animation,
curve: Curves.linearToEaseOut,
// The curve must be flipped so that the reverse animation doesn't play
// an ease-in curve, which iOS does not use.
reverseCurve: Curves.linearToEaseOut.flipped,
).drive(_kBottomUpTween),
super(key: key);
final Animation<Offset> _positionAnimation;
......@@ -816,8 +820,11 @@ class _CupertinoModalPopupRoute<T> extends PopupRoute<T> {
assert(_animation == null);
_animation = CurvedAnimation(
parent: super.createAnimation(),
curve: Curves.ease,
reverseCurve: Curves.ease.flipped,
// These curves were initially measured from native iOS horizontal page
// route animations and seemed to be a good match here as well.
curve: Curves.linearToEaseOut,
reverseCurve: Curves.linearToEaseOut.flipped,
);
_offsetTween = Tween<Offset>(
begin: const Offset(0.0, 1.0),
......@@ -879,8 +886,11 @@ Future<T> showCupertinoModalPopup<T>({
);
}
final Animatable<double> _dialogTween = Tween<double>(begin: 1.2, end: 1.0)
.chain(CurveTween(curve: Curves.fastOutSlowIn));
// The curve and initial scale values were mostly eyeballed from iOS, however
// they reuse the same animation curve that was modelled after native page
// transitions.
final Animatable<double> _dialogScaleTween = Tween<double>(begin: 1.3, end: 1.0)
.chain(CurveTween(curve: Curves.linearToEaseOut));
Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final CurvedAnimation fadeAnimation = CurvedAnimation(
......@@ -897,7 +907,7 @@ Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double>
opacity: fadeAnimation,
child: ScaleTransition(
child: child,
scale: animation.drive(_dialogTween),
scale: animation.drive(_dialogScaleTween),
),
);
}
......@@ -941,7 +951,8 @@ Future<T> showCupertinoDialog<T>({
context: context,
barrierDismissible: false,
barrierColor: _kModalBarrierColor,
transitionDuration: const Duration(milliseconds: 300),
// This transition duration was eyeballed comparing with iOS
transitionDuration: const Duration(milliseconds: 250),
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return builder(context);
},
......
......@@ -718,19 +718,19 @@ void main() {
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0);
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(530.9, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(470.0, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(426.7, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(374.3, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(365.0, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(337.1, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(334.0, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(325.3, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(321.0, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(320.8, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(319.3, 0.1));
......@@ -745,19 +745,19 @@ void main() {
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(319.3, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(388.4, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(449.3, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(492.6, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(544.9, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(554.2, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(582.1, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(585.2, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(593.9, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(598.2, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(598.5, 0.1));
// Action sheet has disappeared
await tester.pump(const Duration(milliseconds: 60));
......@@ -795,23 +795,23 @@ void main() {
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0);
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(530.9, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(470.0, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(426.7, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(374.3, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(365.0, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(337.1, 0.1));
// Exit animation
await tester.tapAt(const Offset(20.0, 20.0));
await tester.pump(const Duration(milliseconds: 60));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(426.7, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(374.3, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(530.9, 0.1));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(470.0, 0.1));
await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0);
......
......@@ -847,27 +847,27 @@ void main() {
// Enter animation.
await tester.pump();
Transform transform = tester.widget(find.byType(Transform));
expect(transform.transform[0], closeTo(1.2, 0.01));
expect(transform.transform[0], closeTo(1.3, 0.01));
await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform));
expect(transform.transform[0], closeTo(1.182, 0.001));
expect(transform.transform[0], closeTo(1.145, 0.001));
await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform));
expect(transform.transform[0], closeTo(1.108, 0.001));
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.044, 0.001));
expect(transform.transform[0], closeTo(1.013, 0.001));
await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform));
expect(transform.transform[0], closeTo(1.015, 0.001));
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.003, 0.001));
expect(transform.transform[0], closeTo(1.000, 0.001));
await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform));
......@@ -951,23 +951,23 @@ void main() {
// 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));
expect(transition.opacity.value, closeTo(0.500, 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));
expect(transition.opacity.value, closeTo(0.332, 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));
expect(transition.opacity.value, closeTo(0.188, 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));
expect(transition.opacity.value, closeTo(0.081, 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));
expect(transition.opacity.value, closeTo(0.019, 0.001));
await tester.pump(const Duration(milliseconds: 25));
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1);
......
......@@ -328,4 +328,107 @@ void main() {
expect(find.text('route'), findsOneWidget);
expect(find.text('push'), findsNothing);
});
testWidgets('Fullscreen route animates correct transform values over time', (WidgetTester tester) async {
await tester.pumpWidget(
CupertinoApp(
home: Builder(
builder: (BuildContext context) {
return CupertinoButton(
child: const Text('Button'),
onPressed: () {
Navigator.push<void>(context, CupertinoPageRoute<void>(
fullscreenDialog: true,
builder: (BuildContext context) {
return Column(
children: <Widget>[
const Placeholder(),
CupertinoButton(
child: const Text('Close'),
onPressed: () {
Navigator.pop<void>(context);
},
)
],
);
},
));
},
);
}
),
),
);
// Enter animation.
await tester.tap(find.text('Button'));
await tester.pump();
// We use a higher number of intervals since the animation has to scale the
// entire screen.
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(443.7, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(291.9, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(168.2, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(89.5, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(48.1, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(26.1, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(14.3, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(7.41, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(3.0, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(0.0, 0.1));
// Exit animation
await tester.tap(find.text('Close'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(156.3, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(308.1, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(431.7, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(510.4, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(551.8, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(573.8, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(585.6, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(592.6, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(596.9, 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, closeTo(600.0, 0.1));
});
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment