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 { ...@@ -417,9 +417,13 @@ class CupertinoFullscreenDialogTransition extends StatelessWidget {
Key key, Key key,
@required Animation<double> animation, @required Animation<double> animation,
@required this.child, @required this.child,
}) : _positionAnimation = animation }) : _positionAnimation = CurvedAnimation(
.drive(CurveTween(curve: Curves.easeInOut)) parent: animation,
.drive(_kBottomUpTween), 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); super(key: key);
final Animation<Offset> _positionAnimation; final Animation<Offset> _positionAnimation;
...@@ -816,8 +820,11 @@ class _CupertinoModalPopupRoute<T> extends PopupRoute<T> { ...@@ -816,8 +820,11 @@ class _CupertinoModalPopupRoute<T> extends PopupRoute<T> {
assert(_animation == null); assert(_animation == null);
_animation = CurvedAnimation( _animation = CurvedAnimation(
parent: super.createAnimation(), 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>( _offsetTween = Tween<Offset>(
begin: const Offset(0.0, 1.0), begin: const Offset(0.0, 1.0),
...@@ -879,8 +886,11 @@ Future<T> showCupertinoModalPopup<T>({ ...@@ -879,8 +886,11 @@ Future<T> showCupertinoModalPopup<T>({
); );
} }
final Animatable<double> _dialogTween = Tween<double>(begin: 1.2, end: 1.0) // The curve and initial scale values were mostly eyeballed from iOS, however
.chain(CurveTween(curve: Curves.fastOutSlowIn)); // 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) { Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation, Widget child) {
final CurvedAnimation fadeAnimation = CurvedAnimation( final CurvedAnimation fadeAnimation = CurvedAnimation(
...@@ -897,7 +907,7 @@ Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double> ...@@ -897,7 +907,7 @@ Widget _buildCupertinoDialogTransitions(BuildContext context, Animation<double>
opacity: fadeAnimation, opacity: fadeAnimation,
child: ScaleTransition( child: ScaleTransition(
child: child, child: child,
scale: animation.drive(_dialogTween), scale: animation.drive(_dialogScaleTween),
), ),
); );
} }
...@@ -941,7 +951,8 @@ Future<T> showCupertinoDialog<T>({ ...@@ -941,7 +951,8 @@ Future<T> showCupertinoDialog<T>({
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
barrierColor: _kModalBarrierColor, 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) { pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
return builder(context); return builder(context);
}, },
......
...@@ -718,19 +718,19 @@ void main() { ...@@ -718,19 +718,19 @@ void main() {
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0); expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0);
await tester.pump(const Duration(milliseconds: 60)); 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)); 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)); 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)); 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)); 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)); await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(319.3, 0.1)); expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(319.3, 0.1));
...@@ -745,19 +745,19 @@ void main() { ...@@ -745,19 +745,19 @@ void main() {
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(319.3, 0.1)); expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, closeTo(319.3, 0.1));
await tester.pump(const Duration(milliseconds: 60)); 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)); 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)); 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)); 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)); 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 // Action sheet has disappeared
await tester.pump(const Duration(milliseconds: 60)); await tester.pump(const Duration(milliseconds: 60));
...@@ -795,23 +795,23 @@ void main() { ...@@ -795,23 +795,23 @@ void main() {
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0); expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0);
await tester.pump(const Duration(milliseconds: 60)); 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)); 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)); 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 // Exit animation
await tester.tapAt(const Offset(20.0, 20.0)); await tester.tapAt(const Offset(20.0, 20.0));
await tester.pump(const Duration(milliseconds: 60)); await tester.pump(const Duration(milliseconds: 60));
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)); 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)); await tester.pump(const Duration(milliseconds: 60));
expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0); expect(tester.getTopLeft(find.byType(CupertinoActionSheet)).dy, 600.0);
......
...@@ -847,27 +847,27 @@ void main() { ...@@ -847,27 +847,27 @@ void main() {
// Enter animation. // Enter animation.
await tester.pump(); await tester.pump();
Transform transform = tester.widget(find.byType(Transform)); 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)); await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform)); 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)); await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform)); 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)); await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform)); 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)); await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform)); 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)); await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform)); 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)); await tester.pump(const Duration(milliseconds: 50));
transform = tester.widget(find.byType(Transform)); transform = tester.widget(find.byType(Transform));
...@@ -951,23 +951,23 @@ void main() { ...@@ -951,23 +951,23 @@ void main() {
// Exit animation, look at reverse FadeTransition. // Exit animation, look at reverse FadeTransition.
await tester.pump(const Duration(milliseconds: 25)); await tester.pump(const Duration(milliseconds: 25));
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1); 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)); await tester.pump(const Duration(milliseconds: 25));
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1); 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)); await tester.pump(const Duration(milliseconds: 25));
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1); 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)); await tester.pump(const Duration(milliseconds: 25));
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1); 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)); await tester.pump(const Duration(milliseconds: 25));
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1); 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)); await tester.pump(const Duration(milliseconds: 25));
transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1); transition = tester.widgetList(find.byType(FadeTransition)).elementAt(1);
......
...@@ -328,4 +328,107 @@ void main() { ...@@ -328,4 +328,107 @@ void main() {
expect(find.text('route'), findsOneWidget); expect(find.text('route'), findsOneWidget);
expect(find.text('push'), findsNothing); 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