mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Transition Curve Fix (#25488)
* Changes Cupertino page transition curves to make paging animations more similar to those of native iOS
This commit is contained in:
parent
5b3e933c7f
commit
cf4fc966f4
@ -487,6 +487,23 @@ class AnimationController extends Animation<double>
|
||||
return _animateToInternal(target, duration: duration, curve: curve);
|
||||
}
|
||||
|
||||
/// Drives the animation from its current value to target.
|
||||
///
|
||||
/// Returns a [TickerFuture] that completes when the animation is complete.
|
||||
///
|
||||
/// The most recently returned [TickerFuture], if any, is marked as having been
|
||||
/// canceled, meaning the future never completes and its [TickerFuture.orCancel]
|
||||
/// derivative future completes with a [TickerCanceled] error.
|
||||
///
|
||||
/// During the animation, [status] is reported as [AnimationStatus.reverse]
|
||||
/// regardless of whether `target` < [value] or not. At the end of the
|
||||
/// animation, when `target` is reached, [status] is reported as
|
||||
/// [AnimationStatus.dismissed].
|
||||
TickerFuture animateBack(double target, { Duration duration, Curve curve = Curves.linear }) {
|
||||
_direction = _AnimationDirection.reverse;
|
||||
return _animateToInternal(target, duration: duration, curve: curve);
|
||||
}
|
||||
|
||||
TickerFuture _animateToInternal(double target, { Duration duration, Curve curve = Curves.linear, AnimationBehavior animationBehavior }) {
|
||||
final AnimationBehavior behavior = animationBehavior ?? this.animationBehavior;
|
||||
double scale = 1.0;
|
||||
|
@ -316,7 +316,6 @@ class _DecelerateCurve extends Curve {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BOUNCE CURVES
|
||||
|
||||
double _bounce(double t) {
|
||||
@ -536,6 +535,15 @@ class Curves {
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_decelerate.mp4}
|
||||
static const Curve decelerate = _DecelerateCurve._();
|
||||
|
||||
/// A curve that is very steep and linear at the beginning, but quickly flattens out
|
||||
/// and very slowly eases in.
|
||||
///
|
||||
/// By default is the curve used to animate pages on iOS back to their original
|
||||
/// position if a swipe gesture is ended midway through a swipe.
|
||||
///
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/fast_linear_to_slow_ease_in.mp4}
|
||||
static const Cubic fastLinearToSlowEaseIn = Cubic(0.18, 1.0, 0.04, 1.0);
|
||||
|
||||
/// A cubic animation curve that speeds up quickly and ends slowly.
|
||||
///
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease.mp4}
|
||||
@ -546,6 +554,13 @@ class Curves {
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in.mp4}
|
||||
static const Cubic easeIn = Cubic(0.42, 0.0, 1.0, 1.0);
|
||||
|
||||
/// A cubic animation curve that starts starts slowly and ends linearly.
|
||||
///
|
||||
/// The symmetric animation to [linearToEaseOut].
|
||||
///
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_in_to_linear.mp4}
|
||||
static const Cubic easeInToLinear = Cubic(0.67, 0.03, 0.65, 0.09);
|
||||
|
||||
/// A cubic animation curve that starts slowly and ends quickly. This is
|
||||
/// similar to [Curves.easeIn], but with sinusoidal easing for a slightly less
|
||||
/// abrupt beginning and end. Nonetheless, the result is quite gentle and is
|
||||
@ -640,6 +655,13 @@ class Curves {
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_ease_out.mp4}
|
||||
static const Cubic easeOut = Cubic(0.0, 0.0, 0.58, 1.0);
|
||||
|
||||
/// A cubic animation curve that starts linearly and ends slowly.
|
||||
///
|
||||
/// A symmetric animation to [easeInToLinear].
|
||||
///
|
||||
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/linear_to_ease_out.mp4}
|
||||
static const Cubic linearToEaseOut = Cubic(0.35, 0.91, 0.33, 0.97);
|
||||
|
||||
/// A cubic animation curve that starts quickly and ends slowly. This is
|
||||
/// similar to [Curves.easeOut], but with sinusoidal easing for a slightly
|
||||
/// less abrupt beginning and end. Nonetheless, the result is quite gentle and
|
||||
|
@ -4,15 +4,25 @@
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:ui' show lerpDouble;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/animation.dart' show Curves;
|
||||
|
||||
const double _kBackGestureWidth = 20.0;
|
||||
const double _kMinFlingVelocity = 1.0; // Screen widths per second.
|
||||
|
||||
// An eyeballed value for the maximum time it takes for a page to animate forward
|
||||
// if the user releases a page mid swipe.
|
||||
const int _kMaxDroppedSwipePageForwardAnimationTime = 800; // Milliseconds.
|
||||
|
||||
// The maximum time for a page to get reset to it's original position if the
|
||||
// user releases a page mid swipe.
|
||||
const int _kMaxPageBackAnimationTime = 300; // Milliseconds.
|
||||
|
||||
// Barrier color for a Cupertino modal barrier.
|
||||
const Color _kModalBarrierColor = Color(0x6604040F);
|
||||
|
||||
@ -152,7 +162,8 @@ class CupertinoPageRoute<T> extends PageRoute<T> {
|
||||
final bool maintainState;
|
||||
|
||||
@override
|
||||
Duration get transitionDuration => const Duration(milliseconds: 350);
|
||||
// A relatively rigorous eyeball estimation.
|
||||
Duration get transitionDuration => const Duration(milliseconds: 400);
|
||||
|
||||
@override
|
||||
Color get barrierColor => null;
|
||||
@ -346,20 +357,26 @@ class CupertinoPageTransition extends StatelessWidget {
|
||||
@required bool linearTransition,
|
||||
}) : assert(linearTransition != null),
|
||||
_primaryPositionAnimation = (linearTransition ? primaryRouteAnimation :
|
||||
// The curves below have been rigorously derived from plots of native
|
||||
// iOS animation frames. Specifically, a video was taken of a page
|
||||
// transition animation and the distance in each frame that the page
|
||||
// moved was measured. A best fit bezier curve was the fitted to the
|
||||
// point set, which is linearToEaseIn. Conversely, easeInToLinear is the
|
||||
// reflection over the origin of linearToEaseIn.
|
||||
CurvedAnimation(
|
||||
parent: primaryRouteAnimation,
|
||||
curve: Curves.easeOut,
|
||||
reverseCurve: Curves.easeIn,
|
||||
curve: Curves.linearToEaseOut,
|
||||
reverseCurve: Curves.easeInToLinear,
|
||||
)
|
||||
).drive(_kRightMiddleTween),
|
||||
_secondaryPositionAnimation = CurvedAnimation(
|
||||
parent: secondaryRouteAnimation,
|
||||
curve: Curves.easeOut,
|
||||
reverseCurve: Curves.easeIn,
|
||||
curve: Curves.linearToEaseOut,
|
||||
reverseCurve: Curves.easeInToLinear,
|
||||
).drive(_kMiddleLeftTween),
|
||||
_primaryShadowAnimation = CurvedAnimation(
|
||||
parent: primaryRouteAnimation,
|
||||
curve: Curves.easeOut,
|
||||
curve: Curves.linearToEaseOut,
|
||||
).drive(_kGradientShadowTween),
|
||||
super(key: key);
|
||||
|
||||
@ -593,13 +610,32 @@ class _CupertinoBackGestureController<T> {
|
||||
// Fling in the appropriate direction.
|
||||
// AnimationController.fling is guaranteed to
|
||||
// take at least one frame.
|
||||
if (velocity.abs() >= _kMinFlingVelocity) {
|
||||
controller.fling(velocity: -velocity);
|
||||
} else if (controller.value <= 0.5) {
|
||||
controller.fling(velocity: -1.0);
|
||||
//
|
||||
// This curve has been determined through rigorously eyeballing native iOS
|
||||
// animations.
|
||||
const Curve animationCurve = Curves.fastLinearToSlowEaseIn;
|
||||
bool animateForward;
|
||||
|
||||
// If the user releases the page before mid screen with sufficient velocity,
|
||||
// or after mid screen, we should animate the page out. Otherwise, the page
|
||||
// should be animated back in.
|
||||
if (velocity.abs() >= _kMinFlingVelocity)
|
||||
animateForward = velocity > 0 ? false : true;
|
||||
else
|
||||
animateForward = controller.value > 0.5 ? true : false;
|
||||
|
||||
if (animateForward) {
|
||||
// The closer the panel is to dismissing, the shorter the animation is.
|
||||
// We want to cap the animation time, but we want to use a linear curve
|
||||
// to determine it.
|
||||
final int droppedPageForwardAnimationTime = min(lerpDouble(_kMaxDroppedSwipePageForwardAnimationTime, 0, controller.value).floor(),
|
||||
_kMaxPageBackAnimationTime);
|
||||
controller.animateTo(1.0, duration: Duration(milliseconds: droppedPageForwardAnimationTime), curve: animationCurve);
|
||||
} else {
|
||||
controller.fling(velocity: 1.0);
|
||||
final int droppedPageBackAnimationTime = lerpDouble(0, _kMaxDroppedSwipePageForwardAnimationTime, controller.value).floor();
|
||||
controller.animateBack(0.0, duration: Duration(milliseconds: droppedPageBackAnimationTime), curve: animationCurve);
|
||||
}
|
||||
|
||||
assert(controller.isAnimating);
|
||||
assert(controller.status != AnimationStatus.completed);
|
||||
assert(controller.status != AnimationStatus.dismissed);
|
||||
|
@ -139,11 +139,11 @@ void main() {
|
||||
// place.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(332.0129337310791, 13.5),
|
||||
const Offset(337.0234375, 13.5),
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(332.0129337310791, 13.5),
|
||||
const Offset(337.0234375, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
@ -158,15 +158,14 @@ void main() {
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
|
||||
expect(flying(tester, find.text('Page 1')), findsNWidgets(2));
|
||||
|
||||
// Same as LTR but more to the right now.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(367.9870662689209, 13.5),
|
||||
const Offset(362.9765625, 13.5),
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(367.9870662689209, 13.5),
|
||||
const Offset(362.9765625, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
@ -180,19 +179,19 @@ void main() {
|
||||
// The transition's stack is ordered. The bottom middle is inserted first.
|
||||
final RenderParagraph bottomMiddle =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).first);
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFF00070F));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xff00050a));
|
||||
expect(bottomMiddle.text.style.fontWeight, FontWeight.w600);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(
|
||||
tester, flying(tester, find.text('Page 1')).first, 0.8609542846679688);
|
||||
tester, flying(tester, find.text('Page 1')).first, 0.9004602432250977);
|
||||
|
||||
// The top back label is styled exactly the same way. But the opacity tweens
|
||||
// are flipped.
|
||||
final RenderParagraph topBackLabel =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).last);
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF00070F));
|
||||
expect(topBackLabel.text.style.color, const Color(0xff00050a));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w600);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.41);
|
||||
@ -201,20 +200,20 @@ void main() {
|
||||
|
||||
// Move animation further a bit.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFF0073F0));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xff006de4));
|
||||
expect(bottomMiddle.text.style.fontWeight, FontWeight.w400);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0);
|
||||
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF0073F0));
|
||||
expect(topBackLabel.text.style.color, const Color(0xff006de4));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w400);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(
|
||||
tester, flying(tester, find.text('Page 1')).last, 0.8733493089675903);
|
||||
tester, flying(tester, find.text('Page 1')).last, 0.7630139589309692);
|
||||
});
|
||||
|
||||
testWidgets('Font transitions respect themes',
|
||||
@ -231,18 +230,18 @@ void main() {
|
||||
// The transition's stack is ordered. The bottom middle is inserted first.
|
||||
final RenderParagraph bottomMiddle =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).first);
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFFFFF8EF));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xfffffaf4));
|
||||
expect(bottomMiddle.text.style.fontWeight, FontWeight.w600);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.8609542846679688);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.9004602432250977);
|
||||
|
||||
// The top back label is styled exactly the same way. But the opacity tweens
|
||||
// are flipped.
|
||||
final RenderParagraph topBackLabel =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).last);
|
||||
expect(topBackLabel.text.style.color, const Color(0xFFFFF8EF));
|
||||
expect(topBackLabel.text.style.color, const Color(0xfffffaf4));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w600);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.41);
|
||||
@ -251,19 +250,19 @@ void main() {
|
||||
|
||||
// Move animation further a bit.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFFFF9A0E));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xffffa01a));
|
||||
expect(bottomMiddle.text.style.fontWeight, FontWeight.w400);
|
||||
expect(bottomMiddle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomMiddle.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0);
|
||||
|
||||
expect(topBackLabel.text.style.color, const Color(0xFFFF9A0E));
|
||||
expect(topBackLabel.text.style.color, const Color(0xffffa01a));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w400);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.41);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.8733493089675903);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.7630139589309692);
|
||||
});
|
||||
|
||||
testWidgets('Fullscreen dialogs do not create heroes',
|
||||
@ -336,20 +335,20 @@ void main() {
|
||||
// The transition's stack is ordered. The bottom middle is inserted first.
|
||||
final RenderParagraph bottomMiddle =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).first);
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFF00070F));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xff00050a));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(332.0129337310791, 13.5),
|
||||
const Offset(337.0234375, 13.5),
|
||||
);
|
||||
|
||||
// The top back label is styled exactly the same way. But the opacity tweens
|
||||
// are flipped.
|
||||
final RenderParagraph topBackLabel =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).last);
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF00070F));
|
||||
expect(topBackLabel.text.style.color, const Color(0xff00050a));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(332.0129337310791, 13.5),
|
||||
const Offset(337.0234375, 13.5),
|
||||
);
|
||||
}
|
||||
|
||||
@ -382,20 +381,20 @@ void main() {
|
||||
// The transition's stack is ordered. The bottom middle is inserted first.
|
||||
final RenderParagraph bottomMiddle =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).first);
|
||||
expect(bottomMiddle.text.style.color, const Color(0xFF00070F));
|
||||
expect(bottomMiddle.text.style.color, const Color(0xff00050a));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(367.9870662689209, 13.5),
|
||||
const Offset(362.9765625, 13.5),
|
||||
);
|
||||
|
||||
// The top back label is styled exactly the same way. But the opacity tweens
|
||||
// are flipped.
|
||||
final RenderParagraph topBackLabel =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).last);
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF00070F));
|
||||
expect(topBackLabel.text.style.color, const Color(0xff00050a));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(367.9870662689209, 13.5),
|
||||
const Offset(362.9765625, 13.5),
|
||||
);
|
||||
}
|
||||
|
||||
@ -515,19 +514,19 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 47.097110748291016);
|
||||
checkBackgroundBoxHeight(tester, 46.234375);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 61.0267448425293);
|
||||
checkBackgroundBoxHeight(tester, 56.3232741355896);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 78.68475294113159);
|
||||
checkBackgroundBoxHeight(tester, 73.04067611694336);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 88.32722091674805);
|
||||
checkBackgroundBoxHeight(tester, 84.33018499612808);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 93.13018447160721);
|
||||
checkBackgroundBoxHeight(tester, 90.53337162733078);
|
||||
});
|
||||
|
||||
testWidgets('Large transition box shrinks to standard nav bar size',
|
||||
@ -540,19 +539,19 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 92.90288925170898);
|
||||
checkBackgroundBoxHeight(tester, 93.765625);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 78.9732551574707);
|
||||
checkBackgroundBoxHeight(tester, 83.6767258644104);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 61.31524705886841);
|
||||
checkBackgroundBoxHeight(tester, 66.95932388305664);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 51.67277908325195);
|
||||
checkBackgroundBoxHeight(tester, 55.66981500387192);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
checkBackgroundBoxHeight(tester, 46.86981552839279);
|
||||
checkBackgroundBoxHeight(tester, 49.46662837266922);
|
||||
});
|
||||
|
||||
testWidgets('Hero flight removed at the end of page transition',
|
||||
@ -614,16 +613,15 @@ void main() {
|
||||
// Only one exists from the top page. The bottom page has no back chevron.
|
||||
findsOneWidget,
|
||||
);
|
||||
|
||||
// Come in from the right and fade in.
|
||||
checkOpacity(tester, backChevron, 0.0);
|
||||
expect(
|
||||
tester.getTopLeft(backChevron), const Offset(71.94993209838867, 5.0));
|
||||
tester.getTopLeft(backChevron), const Offset(73.078125, 5.0));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
checkOpacity(tester, backChevron, 0.32467134296894073);
|
||||
checkOpacity(tester, backChevron, 0.09497911669313908);
|
||||
expect(
|
||||
tester.getTopLeft(backChevron), const Offset(18.033634185791016, 5.0));
|
||||
tester.getTopLeft(backChevron), const Offset(23.260527312755585, 5.0));
|
||||
});
|
||||
|
||||
testWidgets('First appearance of back chevron fades in from the left in RTL',
|
||||
@ -663,14 +661,14 @@ void main() {
|
||||
checkOpacity(tester, backChevron, 0.0);
|
||||
expect(
|
||||
tester.getTopRight(backChevron),
|
||||
const Offset(694.0500679016113, 5.0),
|
||||
const Offset(692.921875, 5.0),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
checkOpacity(tester, backChevron, 0.32467134296894073);
|
||||
checkOpacity(tester, backChevron, 0.09497911669313908);
|
||||
expect(
|
||||
tester.getTopRight(backChevron),
|
||||
const Offset(747.966365814209, 5.0),
|
||||
const Offset(742.7394726872444, 5.0),
|
||||
);
|
||||
});
|
||||
|
||||
@ -688,7 +686,7 @@ void main() {
|
||||
findsNWidgets(2),
|
||||
);
|
||||
|
||||
checkOpacity(tester, backChevrons.first, 0.8393326997756958);
|
||||
checkOpacity(tester, backChevrons.first, 0.8833301812410355);
|
||||
checkOpacity(tester, backChevrons.last, 0.0);
|
||||
// Both overlap at the same place.
|
||||
expect(tester.getTopLeft(backChevrons.first), const Offset(8.0, 5.0));
|
||||
@ -696,7 +694,7 @@ void main() {
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
checkOpacity(tester, backChevrons.first, 0.0);
|
||||
checkOpacity(tester, backChevrons.last, 0.6276369094848633);
|
||||
checkOpacity(tester, backChevrons.last, 0.4604858811944723);
|
||||
// Still in the same place.
|
||||
expect(tester.getTopLeft(backChevrons.first), const Offset(8.0, 5.0));
|
||||
expect(tester.getTopLeft(backChevrons.last), const Offset(8.0, 5.0));
|
||||
@ -718,7 +716,7 @@ void main() {
|
||||
// There's just 1 in flight because there's no back label on the top page.
|
||||
expect(flying(tester, find.text('Page 1')), findsOneWidget);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.8609542846679688);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.9004602432250977);
|
||||
|
||||
// The middle widget doesn't move.
|
||||
expect(
|
||||
@ -746,7 +744,7 @@ void main() {
|
||||
|
||||
expect(flying(tester, find.text('custom')), findsOneWidget);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('custom')), 0.7655444294214249);
|
||||
checkOpacity(tester, flying(tester, find.text('custom')), 0.828093871474266);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('custom'))),
|
||||
const Offset(16.0, 0.0),
|
||||
@ -772,7 +770,7 @@ void main() {
|
||||
|
||||
expect(flying(tester, find.text('custom')), findsOneWidget);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('custom')), 0.8393326997756958);
|
||||
checkOpacity(tester, flying(tester, find.text('custom')), 0.8833301812410355);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('custom'))),
|
||||
const Offset(684.0, 13.5),
|
||||
@ -809,17 +807,17 @@ void main() {
|
||||
expect(flying(tester, find.text('Page 1')), findsOneWidget);
|
||||
|
||||
// Back label fades out faster.
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.5584745407104492);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.6697911769151688);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1'))),
|
||||
const Offset(24.176071166992188, 13.5),
|
||||
const Offset(30.8125, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.0);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1'))),
|
||||
const Offset(-292.97862243652344, 13.5),
|
||||
const Offset(-262.2321922779083, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
@ -847,10 +845,10 @@ void main() {
|
||||
expect(flying(tester, find.text('Page 1')), findsOneWidget);
|
||||
|
||||
// Back label fades out faster.
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.5584745407104492);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.6697911769151688);
|
||||
expect(
|
||||
tester.getTopRight(flying(tester, find.text('Page 1'))),
|
||||
const Offset(775.8239288330078, 13.5),
|
||||
const Offset(769.1875, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
@ -858,7 +856,7 @@ void main() {
|
||||
expect(
|
||||
tester.getTopRight(flying(tester, find.text('Page 1'))),
|
||||
// >1000. It's now off the screen.
|
||||
const Offset(1092.9786224365234, 13.5),
|
||||
const Offset(1062.2321922779083, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
@ -877,27 +875,27 @@ void main() {
|
||||
// bottom back label fading in.
|
||||
expect(flying(tester, find.text('Page 1')), findsNWidgets(2));
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.8393326997756958);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.8833301812410355);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.0);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(17.905914306640625, 51.58156871795654),
|
||||
const Offset(17.375, 52.39453125),
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(17.905914306640625, 51.58156871795654),
|
||||
const Offset(17.375, 52.39453125),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.6276369094848633);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.4604858811944723);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
|
||||
const Offset(43.278289794921875, 19.23011875152588),
|
||||
const Offset(40.818575382232666, 22.49655644595623),
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
|
||||
const Offset(43.278289794921875, 19.23011875152588),
|
||||
const Offset(40.818575382232666, 22.49655644595623),
|
||||
);
|
||||
});
|
||||
|
||||
@ -918,27 +916,27 @@ void main() {
|
||||
expect(flying(tester, find.text('Back')), findsOneWidget);
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('A title too long to fit')),
|
||||
0.8393326997756958);
|
||||
0.8833301812410355);
|
||||
checkOpacity(tester, flying(tester, find.text('Back')), 0.0);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('A title too long to fit'))),
|
||||
const Offset(17.905914306640625, 51.58156871795654),
|
||||
const Offset(17.375, 52.39453125),
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Back'))),
|
||||
const Offset(17.905914306640625, 51.58156871795654),
|
||||
const Offset(17.375, 52.39453125),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
checkOpacity(tester, flying(tester, find.text('A title too long to fit')), 0.0);
|
||||
checkOpacity(tester, flying(tester, find.text('Back')), 0.6276369094848633);
|
||||
checkOpacity(tester, flying(tester, find.text('Back')), 0.4604858811944723);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('A title too long to fit'))),
|
||||
const Offset(43.278289794921875, 19.23011875152588),
|
||||
const Offset(40.818575382232666, 22.49655644595623),
|
||||
);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Back'))),
|
||||
const Offset(43.278289794921875, 19.23011875152588),
|
||||
const Offset(40.818575382232666, 22.49655644595623),
|
||||
);
|
||||
});
|
||||
|
||||
@ -956,30 +954,30 @@ void main() {
|
||||
// The transition's stack is ordered. The bottom large title is inserted first.
|
||||
final RenderParagraph bottomLargeTitle =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).first);
|
||||
expect(bottomLargeTitle.text.style.color, const Color(0xFF00070F));
|
||||
expect(bottomLargeTitle.text.style.color, const Color(0xff00050a));
|
||||
expect(bottomLargeTitle.text.style.fontWeight, FontWeight.w700);
|
||||
expect(bottomLargeTitle.text.style.fontFamily, '.SF Pro Display');
|
||||
expect(bottomLargeTitle.text.style.letterSpacing, 0.36116094589233394);
|
||||
expect(bottomLargeTitle.text.style.letterSpacing, 0.374765625);
|
||||
|
||||
// The top back label is styled exactly the same way.
|
||||
final RenderParagraph topBackLabel =
|
||||
tester.renderObject(flying(tester, find.text('Page 1')).last);
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF00070F));
|
||||
expect(topBackLabel.text.style.color, const Color(0xff00050a));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w700);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Display');
|
||||
expect(topBackLabel.text.style.letterSpacing, 0.36116094589233394);
|
||||
expect(topBackLabel.text.style.letterSpacing, 0.374765625);
|
||||
|
||||
// Move animation further a bit.
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(bottomLargeTitle.text.style.color, const Color(0xFF0073F0));
|
||||
expect(bottomLargeTitle.text.style.color, const Color(0xff006de4));
|
||||
expect(bottomLargeTitle.text.style.fontWeight, FontWeight.w400);
|
||||
expect(bottomLargeTitle.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(bottomLargeTitle.text.style.letterSpacing, -0.3647452166676521);
|
||||
expect(bottomLargeTitle.text.style.letterSpacing, -0.32379547566175454);
|
||||
|
||||
expect(topBackLabel.text.style.color, const Color(0xFF0073F0));
|
||||
expect(topBackLabel.text.style.color, const Color(0xff006de4));
|
||||
expect(topBackLabel.text.style.fontWeight, FontWeight.w400);
|
||||
expect(topBackLabel.text.style.fontFamily, '.SF Pro Text');
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.3647452166676521);
|
||||
expect(topBackLabel.text.style.letterSpacing, -0.32379547566175454);
|
||||
});
|
||||
|
||||
testWidgets('Top middle fades in and slides in from the right',
|
||||
@ -996,15 +994,15 @@ void main() {
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(726.1760711669922, 13.5),
|
||||
const Offset(732.8125, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.6972532719373703);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.5555618554353714);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(409.02137756347656, 13.5),
|
||||
const Offset(439.7678077220917, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
@ -1023,15 +1021,15 @@ void main() {
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
|
||||
expect(
|
||||
tester.getTopRight(flying(tester, find.text('Page 2'))),
|
||||
const Offset(73.82392883300781, 13.5),
|
||||
const Offset(67.1875, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.6972532719373703);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.5555618554353714);
|
||||
expect(
|
||||
tester.getTopRight(flying(tester, find.text('Page 2'))),
|
||||
const Offset(390.97862243652344, 13.5),
|
||||
const Offset(360.2321922779083, 13.5),
|
||||
);
|
||||
});
|
||||
|
||||
@ -1050,15 +1048,15 @@ void main() {
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(768.3521423339844, 54.0),
|
||||
const Offset(781.625, 54.0),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.6753286570310593);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.5292819738388062);
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(134.04275512695312, 54.0),
|
||||
const Offset(195.53561544418335, 54.0),
|
||||
);
|
||||
});
|
||||
|
||||
@ -1078,15 +1076,15 @@ void main() {
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
|
||||
expect(
|
||||
tester.getTopRight(flying(tester, find.text('Page 2'))),
|
||||
const Offset(31.647857666015625, 54.0),
|
||||
const Offset(18.375, 54.0),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 150));
|
||||
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.6753286570310593);
|
||||
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.5292819738388062);
|
||||
expect(
|
||||
tester.getTopRight(flying(tester, find.text('Page 2'))),
|
||||
const Offset(665.9572448730469, 54.0),
|
||||
const Offset(604.4643845558167, 54.0),
|
||||
);
|
||||
});
|
||||
|
||||
@ -1171,7 +1169,6 @@ void main() {
|
||||
await gesture.up();
|
||||
|
||||
await tester.pump();
|
||||
|
||||
// Transition continues.
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
@ -1180,7 +1177,7 @@ void main() {
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(721.8727767467499, 13.5),
|
||||
const Offset(749.6335566043854, 13.5),
|
||||
);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
@ -1229,7 +1226,7 @@ void main() {
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
expect(
|
||||
tester.getTopLeft(flying(tester, find.text('Page 2'))),
|
||||
const Offset(351.00985169410706, 13.5),
|
||||
const Offset(350.0011436641216, 13.5),
|
||||
);
|
||||
|
||||
// Finish the snap back animation.
|
||||
|
@ -40,6 +40,9 @@ void main() {
|
||||
// Page 2 is coming in from the right.
|
||||
expect(widget2TopLeft.dx, greaterThan(widget1InitialTopLeft.dx));
|
||||
|
||||
// Will need to be changed if the animation curve or duration changes.
|
||||
expect(widget1TransientTopLeft.dx, closeTo(130, 1.0));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Page 2 covers page 1.
|
||||
@ -62,6 +65,9 @@ void main() {
|
||||
// Page 2 is leaving towards the right.
|
||||
expect(widget2TopLeft.dx, greaterThan(widget1InitialTopLeft.dx));
|
||||
|
||||
// Will need to be changed if the animation curve or duration changes.
|
||||
expect(widget1TransientTopLeft.dx, closeTo(249, 1.0));
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Page 1'), isOnstage);
|
||||
@ -222,7 +228,7 @@ void main() {
|
||||
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await tester.pump(const Duration(seconds: 1));
|
||||
|
||||
// Page 2 covers page 1.
|
||||
expect(find.text('Page 1'), findsNothing);
|
||||
@ -285,7 +291,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await tester.pump(const Duration(seconds: 2));
|
||||
|
||||
tester.state<NavigatorState>(find.byType(Navigator)).push(
|
||||
CupertinoPageRoute<void>(
|
||||
@ -293,7 +299,7 @@ void main() {
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Page 1'), findsNothing);
|
||||
expect(find.text('Page 2'), isOnstage);
|
||||
@ -332,7 +338,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
tester.state<NavigatorState>(find.byType(Navigator)).push(
|
||||
CupertinoPageRoute<void>(
|
||||
@ -341,7 +347,7 @@ void main() {
|
||||
);
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Page 1'), findsNothing);
|
||||
expect(find.text('Page 2'), isOnstage);
|
||||
@ -379,7 +385,7 @@ void main() {
|
||||
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/next');
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Page 2 covers page 1.
|
||||
expect(find.text('Page 1'), findsNothing);
|
||||
|
@ -411,7 +411,7 @@ void main() {
|
||||
|
||||
await tester.pageBack();
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Next'), findsOneWidget);
|
||||
expect(find.text('Page 2'), findsNothing);
|
||||
|
Loading…
Reference in New Issue
Block a user