Unverified Commit a6f17e69 authored by Youchen Du's avatar Youchen Du Committed by GitHub

Add option for opting out of enter route snapshotting. (#118086)

* Add option for opting out of enter route snapshotting.

* Fix typo.

* Merge find layers logic.

* Add justification comment on why web is skipped in test.

* Update documentation as suggested.

* Update documentation as suggested.
parent 594333b3
......@@ -157,6 +157,7 @@ class _ZoomPageTransition extends StatelessWidget {
required this.animation,
required this.secondaryAnimation,
required this.allowSnapshotting,
required this.allowEnterRouteSnapshotting,
this.child,
}) : assert(animation != null),
assert(secondaryAnimation != null);
......@@ -207,6 +208,15 @@ class _ZoomPageTransition extends StatelessWidget {
/// [secondaryAnimation].
final Widget? child;
/// Whether to enable snapshotting on the entering route during the
/// transition animation.
///
/// If not specified, defaults to true.
/// If false, the route snapshotting will not be applied to the route being
/// animating into, e.g. when transitioning from route A to route B, B will
/// not be snapshotted.
final bool allowEnterRouteSnapshotting;
@override
Widget build(BuildContext context) {
return DualTransitionBuilder(
......@@ -218,7 +228,7 @@ class _ZoomPageTransition extends StatelessWidget {
) {
return _ZoomEnterTransition(
animation: animation,
allowSnapshotting: allowSnapshotting,
allowSnapshotting: allowSnapshotting && allowEnterRouteSnapshotting,
child: child,
);
},
......@@ -243,7 +253,7 @@ class _ZoomPageTransition extends StatelessWidget {
) {
return _ZoomEnterTransition(
animation: animation,
allowSnapshotting: allowSnapshotting,
allowSnapshotting: allowSnapshotting && allowEnterRouteSnapshotting ,
reverse: true,
child: child,
);
......@@ -596,7 +606,18 @@ class OpenUpwardsPageTransitionsBuilder extends PageTransitionsBuilder {
class ZoomPageTransitionsBuilder extends PageTransitionsBuilder {
/// Constructs a page transition animation that matches the transition used on
/// Android Q.
const ZoomPageTransitionsBuilder();
const ZoomPageTransitionsBuilder({
this.allowEnterRouteSnapshotting = true,
});
/// Whether to enable snapshotting on the entering route during the
/// transition animation.
///
/// If not specified, defaults to true.
/// If false, the route snapshotting will not be applied to the route being
/// animating into, e.g. when transitioning from route A to route B, B will
/// not be snapshotted.
final bool allowEnterRouteSnapshotting;
@override
Widget buildTransitions<T>(
......@@ -610,6 +631,7 @@ class ZoomPageTransitionsBuilder extends PageTransitionsBuilder {
animation: animation,
secondaryAnimation: secondaryAnimation,
allowSnapshotting: route?.allowSnapshotting ?? true,
allowEnterRouteSnapshotting: allowEnterRouteSnapshotting,
child: child,
);
}
......
......@@ -287,6 +287,89 @@ void main() {
}
}, variant: TargetPlatformVariant.only(TargetPlatform.android), skip: kIsWeb); // [intended] rasterization is not used on the web.
testWidgets(
'test page transition (_ZoomPageTransition) with rasterization disables snapshotting for enter route',
(WidgetTester tester) async {
Iterable<Layer> findLayers(Finder of) {
return tester.layerListOf(
find.ancestor(of: of, matching: find.byType(SnapshotWidget)).first,
);
}
bool isTransitioningWithoutSnapshotting(Finder of) {
// When snapshotting is off, the OpacityLayer and TransformLayer will be
// applied directly.
final Iterable<Layer> layers = findLayers(of);
return layers.whereType<OpacityLayer>().length == 1 &&
layers.whereType<TransformLayer>().length == 1;
}
bool isSnapshotted(Finder of) {
final Iterable<Layer> layers = findLayers(of);
// The scrim and the snapshot image are the only two layers.
return layers.length == 2 &&
layers.whereType<OffsetLayer>().length == 1 &&
layers.whereType<PictureLayer>().length == 1;
}
await tester.pumpWidget(
MaterialApp(
routes: <String, WidgetBuilder>{
'/1': (_) => const Material(child: Text('Page 1')),
'/2': (_) => const Material(child: Text('Page 2')),
},
initialRoute: '/1',
builder: (BuildContext context, Widget? child) {
final ThemeData themeData = Theme.of(context);
return Theme(
data: themeData.copyWith(
pageTransitionsTheme: PageTransitionsTheme(
builders: <TargetPlatform, PageTransitionsBuilder>{
...themeData.pageTransitionsTheme.builders,
TargetPlatform.android: const ZoomPageTransitionsBuilder(
allowEnterRouteSnapshotting: false,
),
},
),
),
child: Builder(builder: (_) => child!),
);
},
),
);
final Finder page1Finder = find.text('Page 1');
final Finder page2Finder = find.text('Page 2');
// Page 1 on top.
expect(isSnapshotted(page1Finder), isFalse);
// 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));
expect(isSnapshotted(page1Finder), isTrue);
expect(isTransitioningWithoutSnapshotting(page2Finder), isTrue);
// Page 2 on top.
await tester.pumpAndSettle();
expect(isSnapshotted(page2Finder), isFalse);
// 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));
expect(isTransitioningWithoutSnapshotting(page1Finder), isTrue);
expect(isSnapshotted(page2Finder), isTrue);
// Page 1 on top.
await tester.pumpAndSettle();
expect(isSnapshotted(page1Finder), isFalse);
}, variant: TargetPlatformVariant.only(TargetPlatform.android), skip: kIsWeb); // [intended] rasterization is not used on the web.
testWidgets('test fullscreen dialog transition', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment