| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:flutter/cupertino.dart'; |
| import 'package:flutter/foundation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| void main() { |
| final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized(); |
| |
| testWidgets('Default PageTransitionsTheme platform', (WidgetTester tester) async { |
| await tester.pumpWidget(const MaterialApp(home: Text('home'))); |
| final PageTransitionsTheme theme = Theme.of( |
| tester.element(find.text('home')), |
| ).pageTransitionsTheme; |
| expect(theme.builders, isNotNull); |
| for (final TargetPlatform platform in TargetPlatform.values) { |
| switch (platform) { |
| case TargetPlatform.android: |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| case TargetPlatform.linux: |
| case TargetPlatform.windows: |
| expect( |
| theme.builders[platform], |
| isNotNull, |
| reason: 'theme builder for $platform is null', |
| ); |
| case TargetPlatform.fuchsia: |
| expect( |
| theme.builders[platform], |
| isNull, |
| reason: 'theme builder for $platform is not null', |
| ); |
| } |
| } |
| }); |
| |
| testWidgets( |
| 'Default PageTransitionsTheme builds a CupertinoPageTransition', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| expect( |
| Theme.of(tester.element(find.text('push'))).platform, |
| debugDefaultTargetPlatformOverride, |
| ); |
| expect(find.byType(CupertinoPageTransition), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| expect(find.text('page b'), findsOneWidget); |
| expect(find.byType(CupertinoPageTransition), findsOneWidget); |
| }, |
| variant: const TargetPlatformVariant(<TargetPlatform>{ |
| TargetPlatform.iOS, |
| TargetPlatform.macOS, |
| }), |
| ); |
| |
| testWidgets( |
| 'Default PageTransitionsTheme builds a _FadeForwardsPageTransition for android', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| Finder findFadeForwardsPageTransition() { |
| return find.descendant( |
| of: find.byType(MaterialApp), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_FadeForwardsPageTransition', |
| ), |
| ); |
| } |
| |
| expect( |
| Theme.of(tester.element(find.text('push'))).platform, |
| debugDefaultTargetPlatformOverride, |
| ); |
| expect(findFadeForwardsPageTransition(), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| expect(find.text('page b'), findsOneWidget); |
| expect(findFadeForwardsPageTransition(), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'Default background color when FadeForwardsPageTransitionBuilder is used', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: FadeForwardsPageTransitionsBuilder(), |
| }, |
| ), |
| colorScheme: ThemeData().colorScheme.copyWith(surface: Colors.pink), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| Finder findFadeForwardsPageTransition() { |
| return find.descendant( |
| of: find.byType(MaterialApp), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_FadeForwardsPageTransition', |
| ), |
| ); |
| } |
| |
| expect(findFadeForwardsPageTransition(), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pump(const Duration(milliseconds: 400)); |
| |
| final Finder coloredBoxFinder = find.byType(ColoredBox).last; |
| expect(coloredBoxFinder, findsOneWidget); |
| final ColoredBox coloredBox = tester.widget<ColoredBox>(coloredBoxFinder); |
| expect(coloredBox.color, Colors.pink); |
| |
| await tester.pumpAndSettle(); |
| expect(find.text('page b'), findsOneWidget); |
| expect(findFadeForwardsPageTransition(), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'Override background color in FadeForwardsPageTransitionBuilder', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: FadeForwardsPageTransitionsBuilder( |
| backgroundColor: Colors.lightGreen, |
| ), |
| }, |
| ), |
| colorScheme: ThemeData().colorScheme.copyWith(surface: Colors.pink), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| Finder findFadeForwardsPageTransition() { |
| return find.descendant( |
| of: find.byType(MaterialApp), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_FadeForwardsPageTransition', |
| ), |
| ); |
| } |
| |
| expect(findFadeForwardsPageTransition(), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pump(const Duration(milliseconds: 400)); |
| |
| final Finder coloredBoxFinder = find.byType(ColoredBox).last; |
| expect(coloredBoxFinder, findsOneWidget); |
| final ColoredBox coloredBox = tester.widget<ColoredBox>(coloredBoxFinder); |
| expect(coloredBox.color, Colors.lightGreen); |
| |
| await tester.pumpAndSettle(); |
| expect(find.text('page b'), findsOneWidget); |
| expect(findFadeForwardsPageTransition(), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| group('FadeForwardsPageTransitionsBuilder transitions', () { |
| testWidgets( |
| 'opacity fades out during forward secondary animation', |
| (WidgetTester tester) async { |
| final controller = AnimationController( |
| duration: const Duration(milliseconds: 100), |
| vsync: const TestVSync(), |
| ); |
| addTearDown(controller.dispose); |
| final Animation<double> animation = Tween<double>(begin: 1, end: 0).animate(controller); |
| final Animation<double> secondaryAnimation = Tween<double>( |
| begin: 0, |
| end: 1, |
| ).animate(controller); |
| |
| await tester.pumpWidget( |
| Builder( |
| builder: (BuildContext context) { |
| return const FadeForwardsPageTransitionsBuilder().delegatedTransition!( |
| context, |
| animation, |
| secondaryAnimation, |
| false, |
| const SizedBox(), |
| )!; |
| }, |
| ), |
| ); |
| |
| final RenderAnimatedOpacity? renderOpacity = tester |
| .element(find.byType(SizedBox)) |
| .findAncestorRenderObjectOfType<RenderAnimatedOpacity>(); |
| |
| // Since secondary animation is forward, transition will be reverse between duration 0 to 0.25. |
| controller.value = 0.0; |
| await tester.pump(); |
| expect(renderOpacity?.opacity.value, 1.0); |
| |
| controller.value = 0.25; |
| await tester.pump(); |
| expect(renderOpacity?.opacity.value, 0.0); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'opacity fades in during reverse secondary animaation', |
| (WidgetTester tester) async { |
| final controller = AnimationController( |
| duration: const Duration(milliseconds: 100), |
| vsync: const TestVSync(), |
| ); |
| addTearDown(controller.dispose); |
| final Animation<double> animation = Tween<double>(begin: 0, end: 1).animate(controller); |
| final Animation<double> secondaryAnimation = Tween<double>( |
| begin: 1, |
| end: 0, |
| ).animate(controller); |
| |
| await tester.pumpWidget( |
| Builder( |
| builder: (BuildContext context) { |
| return const FadeForwardsPageTransitionsBuilder().delegatedTransition!( |
| context, |
| animation, |
| secondaryAnimation, |
| false, |
| const SizedBox(), |
| )!; |
| }, |
| ), |
| ); |
| |
| final RenderAnimatedOpacity? renderOpacity = tester |
| .element(find.byType(SizedBox)) |
| .findAncestorRenderObjectOfType<RenderAnimatedOpacity>(); |
| |
| // Since secondary animation is reverse, transition will be forward between duration 0.75 to 1.0. |
| controller.value = 0.75; |
| await tester.pump(); |
| expect(renderOpacity?.opacity.value, 0.0); |
| |
| controller.value = 1.0; |
| await tester.pump(); |
| expect(renderOpacity?.opacity.value, 1.0); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'FadeForwardsPageTransitionBuilder does not use ColoredBox for non-opaque routes', |
| (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: FadeForwardsPageTransitionsBuilder( |
| backgroundColor: Colors.lightGreen, |
| ), |
| }, |
| ), |
| ), |
| home: Builder( |
| builder: (BuildContext context) { |
| return Material( |
| child: TextButton( |
| onPressed: () { |
| Navigator.push( |
| context, |
| PageRouteBuilder<void>( |
| opaque: false, |
| pageBuilder: (_, _, _) { |
| return Material( |
| child: TextButton( |
| onPressed: () { |
| Navigator.push( |
| context, |
| MaterialPageRoute<void>(builder: (_) => const Text('page b')), |
| ); |
| }, |
| child: const Text('push b'), |
| ), |
| ); |
| }, |
| ), |
| ); |
| }, |
| child: const Text('push a'), |
| ), |
| ); |
| }, |
| ), |
| ), |
| ); |
| |
| await tester.tap(find.text('push a')); |
| await tester.pumpAndSettle(); |
| |
| await tester.tap(find.text('push b')); |
| await tester.pump(const Duration(milliseconds: 400)); |
| |
| void findColoredBox() { |
| expect( |
| find.byWidgetPredicate((Widget w) => w is ColoredBox && w.color == Colors.lightGreen), |
| findsNothing, |
| ); |
| } |
| |
| // Check that ColoredBox is not used for non-opaque route. |
| findColoredBox(); |
| |
| await tester.pumpAndSettle(); |
| |
| Navigator.pop(tester.element(find.text('page b'))); |
| |
| await tester.pumpAndSettle(const Duration(milliseconds: 400)); |
| |
| // Check that ColoredBox is not used for non-opaque route |
| findColoredBox(); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| }); |
| |
| testWidgets( |
| 'FadeForwardsPageTransitionBuilder default duration is 800ms', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: FadeForwardsPageTransitionsBuilder(), |
| }, |
| ), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| Finder findFadeForwardsPageTransition() { |
| return find.descendant( |
| of: find.byType(MaterialApp), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_FadeForwardsPageTransition', |
| ), |
| ); |
| } |
| |
| expect(findFadeForwardsPageTransition(), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pump(const Duration(milliseconds: 799)); |
| expect(find.text('page b'), findsNothing); |
| ColoredBox coloredBox = tester.widget(find.byType(ColoredBox).last); |
| expect( |
| coloredBox.color, |
| isNot(Colors.transparent), |
| ); // Color is not transparent during animation. |
| |
| await tester.pump(const Duration(milliseconds: 801)); |
| expect(find.text('page b'), findsOneWidget); |
| coloredBox = tester.widget(find.byType(ColoredBox).last); |
| expect(coloredBox.color, Colors.transparent); // Color is transparent during animation. |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'CupertinoPageTransitionsBuilder default duration is 500ms', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.iOS: CupertinoPageTransitionsBuilder(), |
| }, |
| ), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| expect(find.byType(CupertinoPageTransition), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 499)); |
| expect(tester.hasRunningAnimations, isTrue); |
| |
| await tester.pump(const Duration(milliseconds: 10)); |
| expect(tester.hasRunningAnimations, isFalse); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.iOS), |
| ); |
| |
| testWidgets( |
| 'Animation duration changes accordingly when page transition builder changes', |
| (WidgetTester tester) async { |
| Widget buildApp(PageTransitionsBuilder pageTransitionBuilder) { |
| return MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: pageTransitionBuilder, |
| }, |
| ), |
| ), |
| routes: <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => Material( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| TextButton( |
| child: const Text('pop'), |
| onPressed: () { |
| Navigator.of(context).pop(); |
| }, |
| ), |
| const Text('page b'), |
| ], |
| ), |
| ), |
| }, |
| ); |
| } |
| |
| await tester.pumpWidget(buildApp(const FadeForwardsPageTransitionsBuilder())); |
| |
| Finder findFadeForwardsPageTransition() { |
| return find.descendant( |
| of: find.byType(MaterialApp), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_FadeForwardsPageTransition', |
| ), |
| ); |
| } |
| |
| expect(findFadeForwardsPageTransition(), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pump(const Duration(milliseconds: 799)); |
| expect(find.text('page b'), findsNothing); |
| ColoredBox coloredBox = tester.widget(find.byType(ColoredBox).last); |
| expect( |
| coloredBox.color, |
| isNot(Colors.transparent), |
| ); // The color is not transparent during animation. |
| |
| await tester.pump(const Duration(milliseconds: 801)); |
| expect(find.text('page b'), findsOneWidget); |
| coloredBox = tester.widget(find.byType(ColoredBox).last); |
| expect(coloredBox.color, Colors.transparent); // The color is transparent during animation. |
| |
| await tester.pumpWidget(buildApp(const FadeUpwardsPageTransitionsBuilder())); |
| await tester.pumpAndSettle(); |
| expect( |
| find.descendant( |
| of: find.byType(MaterialApp), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_FadeUpwardsPageTransition', |
| ), |
| ), |
| findsOneWidget, |
| ); |
| await tester.tap(find.text('pop')); |
| await tester.pump(const Duration(milliseconds: 299)); |
| expect(find.text('page b'), findsOneWidget); |
| expect( |
| find.byType(ColoredBox), |
| findsNothing, |
| ); // ColoredBox doesn't exist in FadeUpwardsPageTransition. |
| |
| await tester.pump(const Duration(milliseconds: 301)); |
| expect(find.text('page b'), findsNothing); |
| expect(find.text('push'), findsOneWidget); // The first page |
| expect(find.byType(ColoredBox), findsNothing); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'PageTransitionsTheme override builds a CupertinoPageTransition on android', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: CupertinoPageTransitionsBuilder(), |
| }, |
| ), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| expect( |
| Theme.of(tester.element(find.text('push'))).platform, |
| debugDefaultTargetPlatformOverride, |
| ); |
| expect(find.byType(CupertinoPageTransition), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| expect(find.text('page b'), findsOneWidget); |
| expect(find.byType(CupertinoPageTransition), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'CupertinoPageTransition on android does not block gestures on backswipe', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: CupertinoPageTransitionsBuilder(), |
| }, |
| ), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| expect( |
| Theme.of(tester.element(find.text('push'))).platform, |
| debugDefaultTargetPlatformOverride, |
| ); |
| expect(find.byType(CupertinoPageTransition), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| expect(find.text('page b'), findsOneWidget); |
| expect(find.byType(CupertinoPageTransition), findsOneWidget); |
| |
| await tester.pumpAndSettle(const Duration(minutes: 1)); |
| |
| final TestGesture gesture = await tester.startGesture(const Offset(5.0, 100.0)); |
| await gesture.moveBy(const Offset(400.0, 0.0)); |
| await gesture.up(); |
| await tester.pump(); |
| |
| await tester.pumpAndSettle(const Duration(minutes: 1)); |
| |
| expect(find.text('push'), findsOneWidget); |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| expect(find.text('page b'), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'PageTransitionsTheme override builds a _FadeUpwardsTransition', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: |
| FadeUpwardsPageTransitionsBuilder(), // creates a _FadeUpwardsTransition |
| }, |
| ), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| Finder findFadeUpwardsPageTransition() { |
| return find.descendant( |
| of: find.byType(MaterialApp), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_FadeUpwardsPageTransition', |
| ), |
| ); |
| } |
| |
| expect( |
| Theme.of(tester.element(find.text('push'))).platform, |
| debugDefaultTargetPlatformOverride, |
| ); |
| expect(findFadeUpwardsPageTransition(), findsOneWidget); |
| |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| expect(find.text('page b'), findsOneWidget); |
| expect(findFadeUpwardsPageTransition(), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| Widget boilerplate({ |
| required bool themeAllowSnapshotting, |
| bool secondRouteAllowSnapshotting = true, |
| }) { |
| return MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: ZoomPageTransitionsBuilder( |
| allowSnapshotting: themeAllowSnapshotting, |
| ), |
| }, |
| ), |
| ), |
| onGenerateRoute: (RouteSettings settings) { |
| if (settings.name == '/') { |
| return MaterialPageRoute<Widget>(builder: (_) => const Material(child: Text('Page 1'))); |
| } |
| return MaterialPageRoute<Widget>( |
| builder: (_) => const Material(child: Text('Page 2')), |
| allowSnapshotting: secondRouteAllowSnapshotting, |
| ); |
| }, |
| ); |
| } |
| |
| bool isTransitioningWithSnapshotting(WidgetTester tester, Finder of) { |
| final Iterable<Layer> layers = tester.layerListOf( |
| find.ancestor(of: of, matching: find.byType(SnapshotWidget)).first, |
| ); |
| final hasOneOpacityLayer = layers.whereType<OpacityLayer>().length == 1; |
| final hasOneTransformLayer = layers.whereType<TransformLayer>().length == 1; |
| // When snapshotting is on, the OpacityLayer and TransformLayer will not be |
| // applied directly. |
| return !(hasOneOpacityLayer && hasOneTransformLayer); |
| } |
| |
| testWidgets( |
| 'ZoomPageTransitionsBuilder default route snapshotting behavior', |
| (WidgetTester tester) async { |
| await tester.pumpWidget(boilerplate(themeAllowSnapshotting: true)); |
| |
| final Finder page1 = find.text('Page 1'); |
| final Finder page2 = find.text('Page 2'); |
| |
| // Transitioning from page 1 to page 2. |
| tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/2'); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 50)); |
| |
| // Exiting route should be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page1), isTrue); |
| |
| // Entering route should be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page2), isTrue); |
| |
| await tester.pumpAndSettle(); |
| |
| // Transitioning back from page 2 to page 1. |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 50)); |
| |
| // Exiting route should be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page2), isTrue); |
| |
| // Entering route should be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page1), isTrue); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| skip: kIsWeb, // [intended] rasterization is not used on the web. |
| ); |
| |
| testWidgets( |
| 'ZoomPageTransitionsBuilder.allowSnapshotting can disable route snapshotting', |
| (WidgetTester tester) async { |
| await tester.pumpWidget(boilerplate(themeAllowSnapshotting: false)); |
| |
| final Finder page1 = find.text('Page 1'); |
| final Finder page2 = find.text('Page 2'); |
| |
| // Transitioning from page 1 to page 2. |
| tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/2'); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 50)); |
| |
| // Exiting route should not be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page1), isFalse); |
| |
| // Entering route should not be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page2), isFalse); |
| |
| await tester.pumpAndSettle(); |
| |
| // Transitioning back from page 2 to page 1. |
| tester.state<NavigatorState>(find.byType(Navigator)).pop(); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 50)); |
| |
| // Exiting route should not be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page2), isFalse); |
| |
| // Entering route should not be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page1), isFalse); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| skip: kIsWeb, // [intended] rasterization is not used on the web. |
| ); |
| |
| testWidgets( |
| 'Setting PageRoute.allowSnapshotting to false overrides ZoomPageTransitionsBuilder.allowSnapshotting = true', |
| (WidgetTester tester) async { |
| await tester.pumpWidget( |
| boilerplate(themeAllowSnapshotting: true, secondRouteAllowSnapshotting: false), |
| ); |
| |
| final Finder page1 = find.text('Page 1'); |
| final Finder page2 = find.text('Page 2'); |
| |
| // Transitioning from page 1 to page 2. |
| tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/2'); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 50)); |
| |
| // First route should be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page1), isTrue); |
| |
| // Second route should not be snapshotted. |
| expect(isTransitioningWithSnapshotting(tester, page2), isFalse); |
| |
| await tester.pumpAndSettle(); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| skip: kIsWeb, // [intended] rasterization is not used on the web. |
| ); |
| |
| testWidgets( |
| '_ZoomPageTransition only causes child widget built once', |
| (WidgetTester tester) async { |
| // Regression test for https://github.com/flutter/flutter/issues/58345 |
| |
| var builtCount = 0; |
| |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => StatefulBuilder( |
| builder: (BuildContext context, StateSetter setState) { |
| builtCount++; // Increase [builtCount] each time the widget build |
| return TextButton( |
| child: const Text('pop'), |
| onPressed: () { |
| Navigator.pop(context); |
| }, |
| ); |
| }, |
| ), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: |
| ZoomPageTransitionsBuilder(), // creates a _ZoomPageTransition |
| }, |
| ), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| // No matter push or pop was called, the child widget should built only once. |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| expect(builtCount, 1); |
| |
| await tester.tap(find.text('pop')); |
| await tester.pumpAndSettle(); |
| expect(builtCount, 1); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| |
| testWidgets( |
| 'predictive back gestures pop the route on all platforms regardless of whether their transition handles predictive back', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| expect(find.text('push'), findsOneWidget); |
| expect(find.text('page b'), findsNothing); |
| |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('push'), findsNothing); |
| expect(find.text('page b'), findsOneWidget); |
| |
| // Start a system pop gesture. |
| final ByteData startMessage = const StandardMethodCodec().encodeMethodCall( |
| const MethodCall('startBackGesture', <String, dynamic>{ |
| 'touchOffset': <double>[5.0, 300.0], |
| 'progress': 0.0, |
| 'swipeEdge': 0, // left |
| }), |
| ); |
| await binding.defaultBinaryMessenger.handlePlatformMessage( |
| 'flutter/backgesture', |
| startMessage, |
| (ByteData? _) {}, |
| ); |
| await tester.pump(); |
| |
| expect(find.text('push'), findsNothing); |
| expect(find.text('page b'), findsOneWidget); |
| |
| // Drag the system back gesture far enough to commit. |
| final ByteData updateMessage = const StandardMethodCodec().encodeMethodCall( |
| const MethodCall('updateBackGestureProgress', <String, dynamic>{ |
| 'x': 100.0, |
| 'y': 300.0, |
| 'progress': 0.35, |
| 'swipeEdge': 0, // left |
| }), |
| ); |
| await binding.defaultBinaryMessenger.handlePlatformMessage( |
| 'flutter/backgesture', |
| updateMessage, |
| (ByteData? _) {}, |
| ); |
| await tester.pumpAndSettle(); |
| |
| switch (defaultTargetPlatform) { |
| case TargetPlatform.android: |
| // Shows both pages while doing the "peek" predicitve back transition. |
| expect(find.text('push'), findsOneWidget); |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| case TargetPlatform.linux: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.windows: |
| // Does no transition yet; still shows page b only. |
| expect(find.text('push'), findsNothing); |
| } |
| expect(find.text('page b'), findsOneWidget); |
| |
| // Commit the system back gesture. |
| final ByteData commitMessage = const StandardMethodCodec().encodeMethodCall( |
| const MethodCall('commitBackGesture'), |
| ); |
| await binding.defaultBinaryMessenger.handlePlatformMessage( |
| 'flutter/backgesture', |
| commitMessage, |
| (ByteData? _) {}, |
| ); |
| await tester.pumpAndSettle(); |
| expect(find.text('push'), findsOneWidget); |
| expect(find.text('page b'), findsNothing); |
| }, |
| variant: TargetPlatformVariant.all(), |
| ); |
| |
| testWidgets('predictive back is the default on Android', (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| }; |
| await tester.pumpWidget(MaterialApp(routes: routes)); |
| |
| final ThemeData themeData = Theme.of(tester.element(find.text('push'))); |
| switch (defaultTargetPlatform) { |
| case TargetPlatform.android: |
| expect( |
| themeData.pageTransitionsTheme.builders[defaultTargetPlatform], |
| isA<PredictiveBackPageTransitionsBuilder>(), |
| ); |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| case TargetPlatform.linux: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.windows: |
| expect( |
| themeData.pageTransitionsTheme.builders[defaultTargetPlatform], |
| isNot(isA<PredictiveBackPageTransitionsBuilder>()), |
| ); |
| } |
| }, variant: TargetPlatformVariant.all()); |
| |
| testWidgets('predictive back falls back to FadeForwardsPageTransition', ( |
| WidgetTester tester, |
| ) async { |
| Finder findPredictiveBackPageTransition() { |
| return find.descendant( |
| of: find.byType(PrimaryScrollController), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_PredictiveBackSharedElementPageTransition', |
| ), |
| ); |
| } |
| |
| Finder findFallbackPageTransition() { |
| return find.descendant( |
| of: find.byType(PrimaryScrollController), |
| matching: find.byWidgetPredicate( |
| (Widget w) => '${w.runtimeType}' == '_FadeForwardsPageTransition', |
| ), |
| ); |
| } |
| |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => const Text('page b'), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| routes: routes, |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: PredictiveBackPageTransitionsBuilder(), |
| TargetPlatform.iOS: PredictiveBackPageTransitionsBuilder(), |
| TargetPlatform.macOS: PredictiveBackPageTransitionsBuilder(), |
| TargetPlatform.windows: PredictiveBackPageTransitionsBuilder(), |
| TargetPlatform.linux: PredictiveBackPageTransitionsBuilder(), |
| TargetPlatform.fuchsia: PredictiveBackPageTransitionsBuilder(), |
| }, |
| ), |
| ), |
| ), |
| ); |
| |
| final ThemeData themeData = Theme.of(tester.element(find.text('push'))); |
| expect( |
| themeData.pageTransitionsTheme.builders[defaultTargetPlatform], |
| isA<PredictiveBackPageTransitionsBuilder>(), |
| ); |
| |
| expect(find.text('push'), findsOneWidget); |
| expect(find.text('page b'), findsNothing); |
| |
| await tester.tap(find.text('push')); |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('push'), findsNothing); |
| expect(find.text('page b'), findsOneWidget); |
| |
| // Only Android sends system back gestures. |
| if (defaultTargetPlatform == TargetPlatform.android) { |
| final ByteData startMessage = const StandardMethodCodec().encodeMethodCall( |
| const MethodCall('startBackGesture', <String, dynamic>{ |
| 'touchOffset': <double>[5.0, 300.0], |
| 'progress': 0.0, |
| 'swipeEdge': 0, // left |
| }), |
| ); |
| await binding.defaultBinaryMessenger.handlePlatformMessage( |
| 'flutter/backgesture', |
| startMessage, |
| (ByteData? _) {}, |
| ); |
| await tester.pump(); |
| } |
| |
| switch (defaultTargetPlatform) { |
| case TargetPlatform.android: |
| expect(findPredictiveBackPageTransition(), findsOneWidget); |
| expect(findFallbackPageTransition(), findsNothing); |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| case TargetPlatform.linux: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.windows: |
| expect(findPredictiveBackPageTransition(), findsNothing); |
| expect(findFallbackPageTransition(), findsOneWidget); |
| } |
| |
| expect(find.text('push'), findsNothing); |
| expect(find.text('page b'), findsOneWidget); |
| |
| // Drag the system back gesture far enough to commit. |
| if (defaultTargetPlatform == TargetPlatform.android) { |
| final ByteData updateMessage = const StandardMethodCodec().encodeMethodCall( |
| const MethodCall('updateBackGestureProgress', <String, dynamic>{ |
| 'x': 100.0, |
| 'y': 300.0, |
| 'progress': 0.35, |
| 'swipeEdge': 0, // left |
| }), |
| ); |
| await binding.defaultBinaryMessenger.handlePlatformMessage( |
| 'flutter/backgesture', |
| updateMessage, |
| (ByteData? _) {}, |
| ); |
| await tester.pumpAndSettle(); |
| expect(find.text('push'), findsOneWidget); |
| } else { |
| expect(find.text('push'), findsNothing); |
| } |
| |
| expect(find.text('page b'), findsOneWidget); |
| |
| switch (defaultTargetPlatform) { |
| case TargetPlatform.android: |
| expect(findPredictiveBackPageTransition(), findsNWidgets(2)); |
| expect(findFallbackPageTransition(), findsNothing); |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| case TargetPlatform.linux: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.windows: |
| expect(findPredictiveBackPageTransition(), findsNothing); |
| expect(findFallbackPageTransition(), findsOneWidget); |
| } |
| |
| if (defaultTargetPlatform == TargetPlatform.android) { |
| // Commit the system back gesture on Android. |
| final ByteData commitMessage = const StandardMethodCodec().encodeMethodCall( |
| const MethodCall('commitBackGesture'), |
| ); |
| await binding.defaultBinaryMessenger.handlePlatformMessage( |
| 'flutter/backgesture', |
| commitMessage, |
| (ByteData? _) {}, |
| ); |
| } else { |
| // On other platforms, send a one-off system pop. |
| final ByteData popMessage = const JSONMethodCodec().encodeMethodCall( |
| const MethodCall('popRoute'), |
| ); |
| await binding.defaultBinaryMessenger.handlePlatformMessage( |
| 'flutter/navigation', |
| popMessage, |
| (ByteData? _) {}, |
| ); |
| } |
| await tester.pump(); |
| |
| expect(find.text('push'), findsOneWidget); |
| expect(find.text('page b'), findsOneWidget); |
| |
| switch (defaultTargetPlatform) { |
| case TargetPlatform.android: |
| expect(findPredictiveBackPageTransition(), findsNWidgets(2)); |
| expect(findFallbackPageTransition(), findsNothing); |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| case TargetPlatform.linux: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.windows: |
| expect(findPredictiveBackPageTransition(), findsNothing); |
| expect(findFallbackPageTransition(), findsNWidgets(2)); |
| } |
| |
| await tester.pumpAndSettle(); |
| |
| expect(find.text('push'), findsOneWidget); |
| expect(find.text('page b'), findsNothing); |
| |
| switch (defaultTargetPlatform) { |
| case TargetPlatform.android: |
| expect(findPredictiveBackPageTransition(), findsNothing); |
| expect(findFallbackPageTransition(), findsOneWidget); |
| case TargetPlatform.iOS: |
| case TargetPlatform.macOS: |
| case TargetPlatform.linux: |
| case TargetPlatform.fuchsia: |
| case TargetPlatform.windows: |
| expect(findPredictiveBackPageTransition(), findsNothing); |
| expect(findFallbackPageTransition(), findsOneWidget); |
| } |
| }, variant: TargetPlatformVariant.all()); |
| |
| testWidgets( |
| 'ZoomPageTransitionsBuilder uses theme color during transition effects', |
| (WidgetTester tester) async { |
| // Color that is being tested for presence. |
| const themeTestSurfaceColor = Color.fromARGB(255, 195, 255, 0); |
| |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: Scaffold( |
| appBar: AppBar(title: const Text('Home Page')), |
| body: Center( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| ElevatedButton( |
| onPressed: () { |
| Navigator.pushNamed(context, '/scaffolded'); |
| }, |
| child: const Text('Route with scaffold!'), |
| ), |
| ElevatedButton( |
| onPressed: () { |
| Navigator.pushNamed(context, '/not-scaffolded'); |
| }, |
| child: const Text('Route with NO scaffold!'), |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| '/scaffolded': (BuildContext context) => Material( |
| child: Scaffold( |
| appBar: AppBar(title: const Text('Scaffolded Page')), |
| body: Center( |
| child: ElevatedButton( |
| onPressed: () { |
| Navigator.pop(context); |
| }, |
| child: const Text('Back to home route...'), |
| ), |
| ), |
| ), |
| ), |
| '/not-scaffolded': (BuildContext context) => Material( |
| child: Center( |
| child: ElevatedButton( |
| onPressed: () { |
| Navigator.pop(context); |
| }, |
| child: const Text('Back to home route...'), |
| ), |
| ), |
| ), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| colorScheme: ColorScheme.fromSeed( |
| seedColor: Colors.blue, |
| surface: themeTestSurfaceColor, |
| ), |
| pageTransitionsTheme: PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| // Force all platforms to use ZoomPageTransitionsBuilder to test each one. |
| for (final TargetPlatform platform in TargetPlatform.values) |
| platform: const ZoomPageTransitionsBuilder(), |
| }, |
| ), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| // Go to scaffolded page. |
| await tester.tap(find.text('Route with scaffold!')); |
| |
| // Pump till animation is half-way through. |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 75)); |
| |
| // Verify that the render box is painting the right color for scaffolded pages. |
| final RenderBox scaffoldedRenderBox = tester.firstRenderObject<RenderBox>( |
| find.byType(MaterialApp), |
| ); |
| // Expect the color to be at exactly 12.2% opacity at this time. |
| expect(scaffoldedRenderBox, paints..rect(color: themeTestSurfaceColor.withOpacity(0.122))); |
| |
| await tester.pumpAndSettle(); |
| |
| // Go back home and then go to non-scaffolded page. |
| await tester.tap(find.text('Back to home route...')); |
| await tester.pumpAndSettle(); |
| await tester.tap(find.text('Route with NO scaffold!')); |
| |
| // Pump till animation is half-way through. |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 125)); |
| |
| // Verify that the render box is painting the right color for non-scaffolded pages. |
| final RenderBox nonScaffoldedRenderBox = tester.firstRenderObject<RenderBox>( |
| find.byType(MaterialApp), |
| ); |
| // Expect the color to be at exactly 59.6% opacity at this time. |
| expect(nonScaffoldedRenderBox, paints..rect(color: themeTestSurfaceColor.withOpacity(0.596))); |
| |
| await tester.pumpAndSettle(); |
| |
| // Verify that the transition successfully completed. |
| expect(find.text('Back to home route...'), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.all(), |
| ); |
| |
| testWidgets( |
| 'ZoomPageTransitionsBuilder uses developer-provided color during transition effects if provided', |
| (WidgetTester tester) async { |
| // Color that is being tested for presence. |
| const Color testSurfaceColor = Colors.red; |
| |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: Scaffold( |
| appBar: AppBar(title: const Text('Home Page')), |
| body: Center( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| ElevatedButton( |
| onPressed: () { |
| Navigator.pushNamed(context, '/scaffolded'); |
| }, |
| child: const Text('Route with scaffold!'), |
| ), |
| ElevatedButton( |
| onPressed: () { |
| Navigator.pushNamed(context, '/not-scaffolded'); |
| }, |
| child: const Text('Route with NO scaffold!'), |
| ), |
| ], |
| ), |
| ), |
| ), |
| ), |
| '/scaffolded': (BuildContext context) => Material( |
| child: Scaffold( |
| appBar: AppBar(title: const Text('Scaffolded Page')), |
| body: Center( |
| child: ElevatedButton( |
| onPressed: () { |
| Navigator.pop(context); |
| }, |
| child: const Text('Back to home route...'), |
| ), |
| ), |
| ), |
| ), |
| '/not-scaffolded': (BuildContext context) => Material( |
| child: Center( |
| child: ElevatedButton( |
| onPressed: () { |
| Navigator.pop(context); |
| }, |
| child: const Text('Back to home route...'), |
| ), |
| ), |
| ), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue, surface: Colors.blue), |
| pageTransitionsTheme: PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| // Force all platforms to use ZoomPageTransitionsBuilder to test each one. |
| for (final TargetPlatform platform in TargetPlatform.values) |
| platform: const ZoomPageTransitionsBuilder(backgroundColor: testSurfaceColor), |
| }, |
| ), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| // Go to scaffolded page. |
| await tester.tap(find.text('Route with scaffold!')); |
| |
| // Pump till animation is half-way through. |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 75)); |
| |
| // Verify that the render box is painting the right color for scaffolded pages. |
| final RenderBox scaffoldedRenderBox = tester.firstRenderObject<RenderBox>( |
| find.byType(MaterialApp), |
| ); |
| // Expect the color to be at exactly 12.2% opacity at this time. |
| expect(scaffoldedRenderBox, paints..rect(color: testSurfaceColor.withOpacity(0.122))); |
| |
| await tester.pumpAndSettle(); |
| |
| // Go back home and then go to non-scaffolded page. |
| await tester.tap(find.text('Back to home route...')); |
| await tester.pumpAndSettle(); |
| await tester.tap(find.text('Route with NO scaffold!')); |
| |
| // Pump till animation is half-way through. |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 125)); |
| |
| // Verify that the render box is painting the right color for non-scaffolded pages. |
| final RenderBox nonScaffoldedRenderBox = tester.firstRenderObject<RenderBox>( |
| find.byType(MaterialApp), |
| ); |
| // Expect the color to be at exactly 59.6% opacity at this time. |
| expect(nonScaffoldedRenderBox, paints..rect(color: testSurfaceColor.withOpacity(0.596))); |
| |
| await tester.pumpAndSettle(); |
| |
| // Verify that the transition successfully completed. |
| expect(find.text('Back to home route...'), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.all(), |
| ); |
| |
| testWidgets( |
| 'Can interact with incoming route during FadeForwards back navigation', |
| (WidgetTester tester) async { |
| final routes = <String, WidgetBuilder>{ |
| '/': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('push'), |
| onPressed: () { |
| Navigator.of(context).pushNamed('/b'); |
| }, |
| ), |
| ), |
| '/b': (BuildContext context) => Material( |
| child: TextButton( |
| child: const Text('go back'), |
| onPressed: () { |
| Navigator.of(context).pop(); |
| }, |
| ), |
| ), |
| }; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData( |
| pageTransitionsTheme: const PageTransitionsTheme( |
| builders: <TargetPlatform, PageTransitionsBuilder>{ |
| TargetPlatform.android: FadeForwardsPageTransitionsBuilder(), |
| }, |
| ), |
| colorScheme: ThemeData().colorScheme.copyWith(surface: Colors.pink), |
| ), |
| routes: routes, |
| ), |
| ); |
| |
| expect(find.text('push'), findsOneWidget); |
| expect(find.text('go back'), findsNothing); |
| |
| // Go to the second route. The duration of the FadeForwardsPageTransition |
| // is 800ms. |
| await tester.tap(find.text('push')); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 801)); |
| |
| expect(find.text('push'), findsNothing); |
| expect(find.text('go back'), findsOneWidget); |
| |
| // Tap to go back to the first route. |
| await tester.tap(find.text('go back')); |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 400)); |
| |
| expect(find.text('push'), findsOneWidget); |
| expect(find.text('go back'), findsOneWidget); |
| |
| // In the middle of the transition, tap to go back to the second route. |
| await tester.tap(find.text('push')); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 401)); |
| |
| expect(find.text('push'), findsOneWidget); |
| expect(find.text('go back'), findsOneWidget); |
| |
| await tester.pump(); |
| await tester.pump(const Duration(milliseconds: 400)); |
| |
| expect(find.text('push'), findsNothing); |
| expect(find.text('go back'), findsOneWidget); |
| }, |
| variant: TargetPlatformVariant.only(TargetPlatform.android), |
| ); |
| } |