mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
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.
This commit is contained in:
parent
594333b369
commit
a6f17e697c
|
@ -157,6 +157,7 @@ class _ZoomPageTransition extends StatelessWidget {
|
||||||
required this.animation,
|
required this.animation,
|
||||||
required this.secondaryAnimation,
|
required this.secondaryAnimation,
|
||||||
required this.allowSnapshotting,
|
required this.allowSnapshotting,
|
||||||
|
required this.allowEnterRouteSnapshotting,
|
||||||
this.child,
|
this.child,
|
||||||
}) : assert(animation != null),
|
}) : assert(animation != null),
|
||||||
assert(secondaryAnimation != null);
|
assert(secondaryAnimation != null);
|
||||||
|
@ -207,6 +208,15 @@ class _ZoomPageTransition extends StatelessWidget {
|
||||||
/// [secondaryAnimation].
|
/// [secondaryAnimation].
|
||||||
final Widget? child;
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DualTransitionBuilder(
|
return DualTransitionBuilder(
|
||||||
|
@ -218,7 +228,7 @@ class _ZoomPageTransition extends StatelessWidget {
|
||||||
) {
|
) {
|
||||||
return _ZoomEnterTransition(
|
return _ZoomEnterTransition(
|
||||||
animation: animation,
|
animation: animation,
|
||||||
allowSnapshotting: allowSnapshotting,
|
allowSnapshotting: allowSnapshotting && allowEnterRouteSnapshotting,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -243,7 +253,7 @@ class _ZoomPageTransition extends StatelessWidget {
|
||||||
) {
|
) {
|
||||||
return _ZoomEnterTransition(
|
return _ZoomEnterTransition(
|
||||||
animation: animation,
|
animation: animation,
|
||||||
allowSnapshotting: allowSnapshotting,
|
allowSnapshotting: allowSnapshotting && allowEnterRouteSnapshotting ,
|
||||||
reverse: true,
|
reverse: true,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
|
@ -596,7 +606,18 @@ class OpenUpwardsPageTransitionsBuilder extends PageTransitionsBuilder {
|
||||||
class ZoomPageTransitionsBuilder extends PageTransitionsBuilder {
|
class ZoomPageTransitionsBuilder extends PageTransitionsBuilder {
|
||||||
/// Constructs a page transition animation that matches the transition used on
|
/// Constructs a page transition animation that matches the transition used on
|
||||||
/// Android Q.
|
/// 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
|
@override
|
||||||
Widget buildTransitions<T>(
|
Widget buildTransitions<T>(
|
||||||
|
@ -610,6 +631,7 @@ class ZoomPageTransitionsBuilder extends PageTransitionsBuilder {
|
||||||
animation: animation,
|
animation: animation,
|
||||||
secondaryAnimation: secondaryAnimation,
|
secondaryAnimation: secondaryAnimation,
|
||||||
allowSnapshotting: route?.allowSnapshotting ?? true,
|
allowSnapshotting: route?.allowSnapshotting ?? true,
|
||||||
|
allowEnterRouteSnapshotting: allowEnterRouteSnapshotting,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,6 +287,89 @@ void main() {
|
||||||
}
|
}
|
||||||
}, variant: TargetPlatformVariant.only(TargetPlatform.android), skip: kIsWeb); // [intended] rasterization is not used on the web.
|
}, 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 {
|
testWidgets('test fullscreen dialog transition', (WidgetTester tester) async {
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
const MaterialApp(
|
const MaterialApp(
|
||||||
|
|
Loading…
Reference in a new issue