Update CupertinoPageRoute transition animation curves (#122275)

Update `CupertinoPageRoute` transition animation curves
This commit is contained in:
ivirtex 2023-03-30 17:43:40 +02:00 committed by GitHub
parent 84078c856f
commit 302c087738
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 174 additions and 158 deletions

View file

@ -32,7 +32,7 @@ void main() {
// Tap some row to go to the next page.
await tester.tap(find.text('Buy this cool color').first);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
await expectLater(
find.byType(CupertinoNavigationDemo),

View file

@ -6,6 +6,7 @@
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
export 'dart:ui' show Offset;
@ -1381,6 +1382,26 @@ abstract final class Curves {
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_linear_to_slow_ease_in.mp4}
static const Cubic fastLinearToSlowEaseIn = Cubic(0.18, 1.0, 0.04, 1.0);
/// A curve that starts slowly, speeds up very quickly, and then ends slowly.
///
/// This curve is used by default to animate page transitions used by
/// [CupertinoPageRoute].
///
/// It has been derived from plots of native iOS 16.3
/// animation frames on iPhone 14 Pro Max.
/// Specifically, transition animation positions were measured
/// every frame and plotted against time. Then, a cubic curve was
/// strictly fit to the measured data points.
///
/// {@animation 464 192 https://flutter.github.io/assets-for-api-docs/assets/animation/curve_fast_ease_in_to_slow_ease_out.mp4}
static const ThreePointCubic fastEaseInToSlowEaseOut = ThreePointCubic(
Offset(0.056, 0.024),
Offset(0.108, 0.3085),
Offset(0.198, 0.541),
Offset(0.3655, 1.0),
Offset(0.5465, 0.989),
);
/// 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}

View file

@ -136,7 +136,7 @@ mixin CupertinoRouteTransitionMixin<T> on PageRoute<T> {
@override
// A relatively rigorous eyeball estimation.
Duration get transitionDuration => const Duration(milliseconds: 400);
Duration get transitionDuration => const Duration(milliseconds: 500);
@override
Color? get barrierColor => fullscreenDialog ? null : _kCupertinoPageTransitionBarrierColor;
@ -457,15 +457,9 @@ class CupertinoPageTransition extends StatelessWidget {
(linearTransition
? primaryRouteAnimation
: CurvedAnimation(
// 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.
parent: primaryRouteAnimation,
curve: Curves.linearToEaseOut,
reverseCurve: Curves.easeInToLinear,
curve: Curves.fastEaseInToSlowEaseOut,
reverseCurve: Curves.fastEaseInToSlowEaseOut.flipped,
)
).drive(_kRightMiddleTween),
_secondaryPositionAnimation =

View file

@ -53,7 +53,7 @@ void main() {
));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
// Expect the middle of the title to be exactly in the middle of the screen.
expect(tester.getCenter(find.text('Page 2')).dx, 400.0);
@ -673,7 +673,7 @@ void main() {
));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.byType(CupertinoButton), findsOneWidget);
expect(find.text(String.fromCharCode(CupertinoIcons.back.codePoint)), findsOneWidget);
@ -688,7 +688,7 @@ void main() {
));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.widgetWithText(CupertinoButton, 'Close'), findsOneWidget);
@ -696,14 +696,14 @@ void main() {
await tester.tap(find.text('Close'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.text('Page 2'), findsOneWidget);
await tester.tap(find.text(String.fromCharCode(CupertinoIcons.back.codePoint)));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.text('Home page'), findsOneWidget);
});
@ -1157,7 +1157,7 @@ void main() {
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
tester.state<NavigatorState>(find.byType(Navigator)).push(
CupertinoPageRoute<void>(
@ -1176,11 +1176,11 @@ void main() {
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
await tester.tap(find.byType(CupertinoNavigationBarBackButton));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
// The second page is still on top and didn't pop.
expect(find.text('A Phone'), findsOneWidget);
@ -1406,13 +1406,13 @@ void main() {
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.text('Page 1'), findsNothing);
expect(find.text('Page 2'), findsOneWidget);
await tester.tap(find.text(String.fromCharCode(CupertinoIcons.back.codePoint)));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.text('Page 1'), findsOneWidget);
expect(find.text('Page 2'), findsNothing);
});

View file

@ -51,7 +51,7 @@ Future<void> startTransitionBetween(
));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
tester
.state<NavigatorState>(find.byType(Navigator))
@ -150,11 +150,11 @@ void main() {
// place.
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(337.1953125, 13.5),
const Offset(342.33420100808144, 13.5),
);
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(337.1953125, 13.5),
const Offset(342.33420100808144, 13.5),
);
});
@ -171,11 +171,11 @@ void main() {
// Same as LTR but more to the right now.
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(362.8046875, 13.5),
const Offset(357.66579899191856, 13.5),
);
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(362.8046875, 13.5),
const Offset(357.66579899191856, 13.5),
);
});
@ -209,18 +209,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(0xff00050a));
expect(bottomMiddle.text.style!.color, const Color(0xff000306));
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.9004602432250977);
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.9404401779174805);
// 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(0xff00050a));
expect(topBackLabel.text.style!.color, const Color(0xff000306));
expect(topBackLabel.text.style!.fontWeight, FontWeight.w600);
expect(topBackLabel.text.style!.fontFamily, '.SF Pro Text');
expect(topBackLabel.text.style!.letterSpacing, -0.41);
@ -229,19 +229,19 @@ void main() {
// Move animation further a bit.
await tester.pump(const Duration(milliseconds: 200));
expect(bottomMiddle.text.style!.color, const Color(0xff006de4));
expect(bottomMiddle.text.style!.color, const Color(0xff005ec5));
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(0xff006de4));
expect(topBackLabel.text.style!.color, const Color(0xff005ec5));
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.7630139589309692);
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.5292819738388062);
});
testWidgets('Font transitions respect themes', (WidgetTester tester) async {
@ -257,18 +257,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(0xFFF4F9FF));
expect(bottomMiddle.text.style!.color, const Color(0xfff8fbff));
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.9004602432250977);
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.9404401779174805);
// 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(0xFFF4F9FF));
expect(topBackLabel.text.style!.color, const Color(0xfff8fbff));
expect(topBackLabel.text.style!.fontWeight, FontWeight.w600);
expect(topBackLabel.text.style!.fontFamily, '.SF Pro Text');
expect(topBackLabel.text.style!.letterSpacing, -0.41);
@ -277,19 +277,19 @@ void main() {
// Move animation further a bit.
await tester.pump(const Duration(milliseconds: 200));
expect(bottomMiddle.text.style!.color, const Color(0xFF2390FF));
expect(bottomMiddle.text.style!.color, const Color(0xff409fff));
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(0xFF2390FF));
expect(topBackLabel.text.style!.color, const Color(0xff409fff));
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.7630139589309692);
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.5292819738388062);
});
testWidgets('Fullscreen dialogs do not create heroes', (WidgetTester tester) async {
@ -360,20 +360,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(0xff00050a));
expect(bottomMiddle.text.style!.color, const Color(0xff000306));
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(337.1953125, 13.5),
const Offset(342.33420100808144, 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(0xff00050a));
expect(topBackLabel.text.style!.color, const Color(0xff000306));
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(337.1953125, 13.5),
const Offset(342.33420100808144, 13.5),
);
}
@ -405,20 +405,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(0xff00050a));
expect(bottomMiddle.text.style!.color, const Color(0xff000306));
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(362.8046875, 13.5),
const Offset(357.66579899191856, 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(0xff00050a));
expect(topBackLabel.text.style!.color, const Color(0xff000306));
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(362.8046875, 13.5),
const Offset(357.66579899191856, 13.5),
);
}
@ -555,19 +555,19 @@ void main() {
);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 46.234375);
checkBackgroundBoxHeight(tester, 45.3376561999321);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 56.3232741355896);
checkBackgroundBoxHeight(tester, 51.012951374053955);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 73.04067611694336);
checkBackgroundBoxHeight(tester, 63.06760931015015);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 75.89544230699539);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 84.33018499612808);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 90.53337162733078);
});
testWidgets('Large transition box shrinks to standard nav bar size', (WidgetTester tester) async {
@ -579,19 +579,19 @@ void main() {
);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 93.765625);
checkBackgroundBoxHeight(tester, 94.6623438000679);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 83.6767258644104);
checkBackgroundBoxHeight(tester, 88.98704862594604);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 66.95932388305664);
checkBackgroundBoxHeight(tester, 76.93239068984985);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 64.10455769300461);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 55.66981500387192);
await tester.pump(const Duration(milliseconds: 50));
checkBackgroundBoxHeight(tester, 49.46662837266922);
});
testWidgets('Hero flight removed at the end of page transition', (WidgetTester tester) async {
@ -716,9 +716,9 @@ void main() {
);
// Come in from the right and fade in.
checkOpacity(tester, backChevron, 0.0);
expect(tester.getTopLeft(backChevron), const Offset(86.734375, 7.0));
expect(tester.getTopLeft(backChevron), const Offset(88.04496401548386, 7.0));
await tester.pump(const Duration(milliseconds: 150));
await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, backChevron, 0.09497911669313908);
expect(tester.getTopLeft(backChevron), const Offset(31.055883467197418, 7.0));
});
@ -758,10 +758,10 @@ void main() {
checkOpacity(tester, backChevron, 0.0);
expect(
tester.getTopRight(backChevron),
const Offset(687.265625, 7.0),
const Offset(685.9550359845161, 7.0),
);
await tester.pump(const Duration(milliseconds: 150));
await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, backChevron, 0.09497911669313908);
expect(
tester.getTopRight(backChevron),
@ -781,13 +781,13 @@ void main() {
findsNWidgets(2),
);
checkOpacity(tester, backChevrons.first, 0.8833301812410355);
checkOpacity(tester, backChevrons.first, 0.9280824661254883);
checkOpacity(tester, backChevrons.last, 0.0);
// Both overlap at the same place.
expect(tester.getTopLeft(backChevrons.first), const Offset(14.0, 7.0));
expect(tester.getTopLeft(backChevrons.last), const Offset(14.0, 7.0));
await tester.pump(const Duration(milliseconds: 150));
await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, backChevrons.first, 0.0);
checkOpacity(tester, backChevrons.last, 0.4604858811944723);
// Still in the same place.
@ -810,7 +810,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.9004602432250977);
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.9404401779174805);
// The middle widget doesn't move.
expect(
@ -818,7 +818,7 @@ void main() {
const Offset(400.0, 22.0),
);
await tester.pump(const Duration(milliseconds: 150));
await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.0);
expect(
tester.getCenter(flying(tester, find.text('Page 1'))),
@ -838,7 +838,7 @@ void main() {
expect(flying(tester, find.text('custom')), findsOneWidget);
checkOpacity(tester, flying(tester, find.text('custom')), 0.828093871474266);
checkOpacity(tester, flying(tester, find.text('custom')), 0.8948725312948227);
expect(
tester.getTopLeft(flying(tester, find.text('custom'))),
const Offset(16.0, 0.0),
@ -864,7 +864,7 @@ void main() {
expect(flying(tester, find.text('custom')), findsOneWidget);
checkOpacity(tester, flying(tester, find.text('custom')), 0.8833301812410355);
checkOpacity(tester, flying(tester, find.text('custom')), 0.9280824661254883);
expect(
tester.getTopLeft(flying(tester, find.text('custom'))),
const Offset(684.0, 13.5),
@ -900,13 +900,13 @@ void main() {
expect(flying(tester, find.text('Page 1')), findsOneWidget);
// Back label fades out faster.
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.6697911769151688);
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.7952219992876053);
expect(
tester.getTopLeft(flying(tester, find.text('Page 1'))),
const Offset(34.8125, 13.5),
const Offset(41.71033692359924, 13.5),
);
await tester.pump(const Duration(milliseconds: 150));
await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.0);
expect(
tester.getTopLeft(flying(tester, find.text('Page 1'))),
@ -937,13 +937,13 @@ void main() {
expect(flying(tester, find.text('Page 1')), findsOneWidget);
// Back label fades out faster.
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.6697911769151688);
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.7952219992876053);
expect(
tester.getTopRight(flying(tester, find.text('Page 1'))),
const Offset(765.1875, 13.5),
const Offset(758.2896630764008, 13.5),
);
await tester.pump(const Duration(milliseconds: 150));
await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, flying(tester, find.text('Page 1')), 0.0);
expect(
tester.getTopRight(flying(tester, find.text('Page 1'))),
@ -966,18 +966,18 @@ 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.8833301812410355);
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.9280824661254883);
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.0);
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).first),
const Offset(17.546875, 52.39453125),
const Offset(16.926069676876068, 52.73951627314091),
);
expect(
tester.getTopLeft(flying(tester, find.text('Page 1')).last),
const Offset(17.546875, 52.39453125),
const Offset(16.926069676876068, 52.73951627314091),
);
await tester.pump(const Duration(milliseconds: 150));
await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, flying(tester, find.text('Page 1')).first, 0.0);
checkOpacity(tester, flying(tester, find.text('Page 1')).last, 0.4604858811944723);
expect(
@ -1004,18 +1004,18 @@ void main() {
// Automatically changed to the word 'Back' in the back label.
expect(flying(tester, find.text('Back')), findsOneWidget);
checkOpacity(tester, flying(tester, find.text('A title too long to fit')), 0.8833301812410355);
checkOpacity(tester, flying(tester, find.text('A title too long to fit')), 0.9280824661254883);
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.546875, 52.39453125),
const Offset(16.926069676876068, 52.73951627314091),
);
expect(
tester.getTopLeft(flying(tester, find.text('Back'))),
const Offset(17.546875, 52.39453125),
const Offset(16.926069676876068, 52.73951627314091),
);
await tester.pump(const Duration(milliseconds: 150));
await tester.pump(const Duration(milliseconds: 200));
checkOpacity(tester, flying(tester, find.text('A title too long to fit')), 0.0);
checkOpacity(tester, flying(tester, find.text('Back')), 0.4604858811944723);
expect(
@ -1041,30 +1041,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(0xff00050a));
expect(bottomLargeTitle.text.style!.color, const Color(0xff000306));
expect(bottomLargeTitle.text.style!.fontWeight, FontWeight.w700);
expect(bottomLargeTitle.text.style!.fontFamily, '.SF Pro Display');
expect(bottomLargeTitle.text.style!.letterSpacing, moreOrLessEquals(0.374765625));
expect(bottomLargeTitle.text.style!.letterSpacing, moreOrLessEquals(0.38890619069337845));
// 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(0xff00050a));
expect(topBackLabel.text.style!.color, const Color(0xff000306));
expect(topBackLabel.text.style!.fontWeight, FontWeight.w700);
expect(topBackLabel.text.style!.fontFamily, '.SF Pro Display');
expect(topBackLabel.text.style!.letterSpacing, moreOrLessEquals(0.374765625));
expect(topBackLabel.text.style!.letterSpacing, moreOrLessEquals(0.38890619069337845));
// Move animation further a bit.
await tester.pump(const Duration(milliseconds: 200));
expect(bottomLargeTitle.text.style!.color, const Color(0xff006de4));
expect(bottomLargeTitle.text.style!.fontWeight, FontWeight.w400);
expect(bottomLargeTitle.text.style!.color, const Color(0xff005ec5));
expect(bottomLargeTitle.text.style!.fontWeight, FontWeight.w500);
expect(bottomLargeTitle.text.style!.fontFamily, '.SF Pro Text');
expect(bottomLargeTitle.text.style!.letterSpacing, moreOrLessEquals(-0.32379547566175454));
expect(bottomLargeTitle.text.style!.letterSpacing, moreOrLessEquals(-0.2259759941697121));
expect(topBackLabel.text.style!.color, const Color(0xff006de4));
expect(topBackLabel.text.style!.fontWeight, FontWeight.w400);
expect(topBackLabel.text.style!.color, const Color(0xff005ec5));
expect(topBackLabel.text.style!.fontWeight, FontWeight.w500);
expect(topBackLabel.text.style!.fontFamily, '.SF Pro Text');
expect(topBackLabel.text.style!.letterSpacing, moreOrLessEquals(-0.32379547566175454));
expect(topBackLabel.text.style!.letterSpacing, moreOrLessEquals(-0.2259759941697121));
});
testWidgets('Top middle fades in and slides in from the right', (WidgetTester tester) async {
@ -1080,15 +1080,15 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(732.8125, 13.5),
const Offset(739.7103369235992, 13.5),
);
await tester.pump(const Duration(milliseconds: 150));
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.5555618554353714);
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.29867843724787235);
expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(439.7678077220917, 13.5),
const Offset(504.65044379234314, 13.5),
);
});
@ -1130,15 +1130,15 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
expect(
tester.getTopRight(flying(tester, find.text('Page 2'))),
const Offset(67.1875, 13.5),
const Offset(60.28966307640076, 13.5),
);
await tester.pump(const Duration(milliseconds: 150));
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.5555618554353714);
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.29867843724787235);
expect(
tester.getTopRight(flying(tester, find.text('Page 2'))),
const Offset(360.2321922779083, 13.5),
const Offset(295.34955620765686, 13.5),
);
});
@ -1156,15 +1156,15 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(781.625, 54.0),
const Offset(795.4206738471985, 54.0),
);
await tester.pump(const Duration(milliseconds: 150));
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.5292819738388062);
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.2601277381181717);
expect(
tester.getTopLeft(flying(tester, find.text('Page 2'))),
const Offset(195.53561544418335, 54.0),
const Offset(325.3008875846863, 54.0),
);
});
@ -1183,15 +1183,15 @@ void main() {
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.0);
expect(
tester.getTopRight(flying(tester, find.text('Page 2'))),
const Offset(18.375, 54.0),
const Offset(4.579326152801514, 54.0),
);
await tester.pump(const Duration(milliseconds: 150));
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.5292819738388062);
checkOpacity(tester, flying(tester, find.text('Page 2')), 0.2601277381181717);
expect(
tester.getTopRight(flying(tester, find.text('Page 2'))),
const Offset(604.4643845558167, 54.0),
const Offset(474.6991124153137, 54.0),
);
});
@ -1248,10 +1248,10 @@ void main() {
);
// Go to the next page.
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
// Start the gesture at the edge of the screen.
final TestGesture gesture = await tester.startGesture(const Offset(5.0, 200.0));
final TestGesture gesture = await tester.startGesture(const Offset(5.0, 200.0));
// Trigger the swipe.
await gesture.moveBy(const Offset(100.0, 0.0));
@ -1310,7 +1310,7 @@ void main() {
);
// Go to the next page.
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
// Start the gesture at the edge of the screen.
final TestGesture gesture = await tester.startGesture(const Offset(5.0, 200.0));

View file

@ -41,7 +41,7 @@ void main() {
expect(widget2TopLeft.dx, greaterThan(widget1InitialTopLeft.dx));
// Will need to be changed if the animation curve or duration changes.
expect(widget1TransientTopLeft.dx, moreOrLessEquals(130, epsilon: 1.0));
expect(widget1TransientTopLeft.dx, moreOrLessEquals(158, epsilon: 1.0));
await tester.pumpAndSettle();
@ -66,7 +66,7 @@ void main() {
expect(widget2TopLeft.dx, greaterThan(widget1InitialTopLeft.dx));
// Will need to be changed if the animation curve or duration changes.
expect(widget1TransientTopLeft.dx, moreOrLessEquals(249, epsilon: 1.0));
expect(widget1TransientTopLeft.dx, moreOrLessEquals(220, epsilon: 1.0));
await tester.pumpAndSettle();

View file

@ -138,7 +138,7 @@ void main() {
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
tester.state<NavigatorState>(find.byType(Navigator)).push(
CupertinoPageRoute<void>(
@ -153,7 +153,7 @@ void main() {
);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.widgetWithText(CupertinoNavigationBar, 'A Phone'), findsOneWidget);
expect(tester.getCenter(find.text('A Phone')).dx, 400.0);
@ -415,65 +415,66 @@ void main() {
// entire screen.
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(443.7, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(475.6, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(291.9, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(350.0, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(168.2, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(237.4, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(149.2, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(89.5, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(48.1, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(54.4, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(26.1, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(33.2, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(14.3, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(20.4, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(7.41, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(12.6, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(3.0, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(7.4, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(0.0, epsilon: 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, moreOrLessEquals(156.3, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(411.03, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(308.1, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(484.35, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(431.7, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(530.67, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(510.4, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(557.61, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(551.8, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(573.88, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(573.8, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(583.86, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(585.6, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(590.26, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(592.6, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(594.58, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(596.9, epsilon: 0.1));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(597.66, epsilon: 0.1));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dy, moreOrLessEquals(600.0, epsilon: 0.1));
@ -520,41 +521,41 @@ void main() {
// entire screen.
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-70.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-55.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-137.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-111.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-192.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-161.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-227.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-200.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-246.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-226.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-255.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-242.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-260.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-251.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-264.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-257.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-266.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-261.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-267.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-263.0, epsilon: 1.0));
// Exit animation
await tester.tap(find.text('Close'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 40));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-198.0, epsilon: 1.0));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-83.0, epsilon: 1.0));
await tester.pump(const Duration(milliseconds: 360));
expect(tester.getTopLeft(find.byType(Placeholder)).dx, moreOrLessEquals(-0.0, epsilon: 1.0));
@ -673,40 +674,40 @@ void main() {
);
tester.state<NavigatorState>(find.byType(Navigator)).push(route2);
// The whole transition is 400ms based on CupertinoPageRoute.transitionDuration.
// The whole transition is 500ms based on CupertinoPageRoute.transitionDuration.
// Break it up into small chunks.
await tester.pump();
await tester.pump(const Duration(milliseconds: 50));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-87, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(537, epsilon: 1));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-69, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(609, epsilon: 1));
await tester.pump(const Duration(milliseconds: 50));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-166, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(301, epsilon: 1));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-136, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(362, epsilon: 1));
await tester.pump(const Duration(milliseconds: 50));
// Translation slows down as time goes on.
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-220, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(141, epsilon: 1));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-191, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(192, epsilon: 1));
// Finish the rest of the animation
await tester.pump(const Duration(milliseconds: 250));
await tester.pump(const Duration(milliseconds: 350));
tester.state<NavigatorState>(find.byType(Navigator)).pop();
await tester.pump();
await tester.pump(const Duration(milliseconds: 50));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-179, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(262, epsilon: 1));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-197, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(190, epsilon: 1));
await tester.pump(const Duration(milliseconds: 50));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-100, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(499, epsilon: 1));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-129, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(437, epsilon: 1));
await tester.pump(const Duration(milliseconds: 50));
// Translation slows down as time goes on.
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-47, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(659, epsilon: 1));
expect(tester.getTopLeft(find.text('1')).dx, moreOrLessEquals(-74, epsilon: 1));
expect(tester.getTopLeft(find.text('2')).dx, moreOrLessEquals(607, epsilon: 1));
});
testWidgets('Dragged pop gesture is linear', (WidgetTester tester) async {
@ -1060,7 +1061,7 @@ void main() {
expect(box, paintsShadowRect(dx: 468, color: const Color(0x01000000)));
expect(box, paintsShadowRect(dx: 458, color: const Color(0x00000000)));
await tester.pump(const Duration(milliseconds: 250));
await tester.pump(const Duration(milliseconds: 150));
// At the end of the animation, the shadow approaches full transparency
expect(box, paintsShadowRect(dx: 794, color: const Color(0x01000000)));

View file

@ -364,7 +364,7 @@ void main() {
// Navigate in tab 2.
await tester.tap(find.text('Next'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.text('Page 2 of tab 2'), isOnstage);
expect(find.text('Page 1 of tab 1', skipOffstage: false), isOffstage);
@ -379,7 +379,7 @@ void main() {
// Navigate in tab 1.
await tester.tap(find.text('Next'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.text('Page 2 of tab 1'), isOnstage);
expect(find.text('Page 2 of tab 2', skipOffstage: false), isOffstage);
@ -393,7 +393,7 @@ void main() {
// Pop in tab 2
await tester.tap(find.text('Back'));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 600));
expect(find.text('Page 1 of tab 2'), isOnstage);
expect(find.text('Page 2 of tab 1', skipOffstage: false), isOffstage);