[framework] re-rasterize page transition when layout size changes (#115371)
* [framework] autoresize on snapshot widget * ++ * ++ * ++ * use layout to resize
This commit is contained in:
parent
b57910927f
commit
d01874d71e
@ -291,7 +291,6 @@ class _ZoomEnterTransitionState extends State<_ZoomEnterTransition> with _ZoomTr
|
||||
bool get useSnapshot => !kIsWeb && widget.allowSnapshotting;
|
||||
|
||||
late _ZoomEnterTransitionPainter delegate;
|
||||
MediaQueryData? mediaQueryData;
|
||||
|
||||
static final Animatable<double> _fadeInTransition = Tween<double>(
|
||||
begin: 0.0,
|
||||
@ -356,18 +355,6 @@ class _ZoomEnterTransitionState extends State<_ZoomEnterTransition> with _ZoomTr
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
// If the screen size changes during the transition, perhaps due to
|
||||
// a keyboard dismissal, then ensure that contents are re-rasterized once.
|
||||
final MediaQueryData? data = MediaQuery.maybeOf(context);
|
||||
if (mediaQueryDataChanged(mediaQueryData, data)) {
|
||||
controller.clear();
|
||||
}
|
||||
mediaQueryData = data;
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.animation.removeListener(onAnimationValueChange);
|
||||
@ -382,6 +369,7 @@ class _ZoomEnterTransitionState extends State<_ZoomEnterTransition> with _ZoomTr
|
||||
painter: delegate,
|
||||
controller: controller,
|
||||
mode: SnapshotMode.permissive,
|
||||
autoresize: true,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
@ -407,7 +395,6 @@ class _ZoomExitTransition extends StatefulWidget {
|
||||
|
||||
class _ZoomExitTransitionState extends State<_ZoomExitTransition> with _ZoomTransitionBase {
|
||||
late _ZoomExitTransitionPainter delegate;
|
||||
MediaQueryData? mediaQueryData;
|
||||
|
||||
// See SnapshotWidget doc comment, this is disabled on web because the HTML backend doesn't
|
||||
// support this functionality and the canvaskit backend uses a single thread for UI and raster
|
||||
@ -472,18 +459,6 @@ class _ZoomExitTransitionState extends State<_ZoomExitTransition> with _ZoomTran
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
// If the screen size changes during the transition, perhaps due to
|
||||
// a keyboard dismissal, then ensure that contents are re-rasterized once.
|
||||
final MediaQueryData? data = MediaQuery.maybeOf(context);
|
||||
if (mediaQueryDataChanged(mediaQueryData, data)) {
|
||||
controller.clear();
|
||||
}
|
||||
mediaQueryData = data;
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.animation.removeListener(onAnimationValueChange);
|
||||
@ -498,6 +473,7 @@ class _ZoomExitTransitionState extends State<_ZoomExitTransition> with _ZoomTran
|
||||
painter: delegate,
|
||||
controller: controller,
|
||||
mode: SnapshotMode.permissive,
|
||||
autoresize: true,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
@ -830,13 +806,6 @@ mixin _ZoomTransitionBase {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Whether any of the properties that would impact the page transition
|
||||
// changed.
|
||||
bool mediaQueryDataChanged(MediaQueryData? oldData, MediaQueryData? newData) {
|
||||
return oldData?.size != newData?.size ||
|
||||
oldData?.viewInsets != newData?.viewInsets;
|
||||
}
|
||||
}
|
||||
|
||||
class _ZoomEnterTransitionPainter extends SnapshotPainter {
|
||||
|
@ -110,6 +110,7 @@ class SnapshotWidget extends SingleChildRenderObjectWidget {
|
||||
super.key,
|
||||
this.mode = SnapshotMode.normal,
|
||||
this.painter = const _DefaultSnapshotPainter(),
|
||||
this.autoresize = false,
|
||||
required this.controller,
|
||||
required super.child
|
||||
});
|
||||
@ -125,6 +126,12 @@ class SnapshotWidget extends SingleChildRenderObjectWidget {
|
||||
/// See [SnapshotMode] for more information.
|
||||
final SnapshotMode mode;
|
||||
|
||||
/// Whether or not changes in render object size should automatically re-create
|
||||
/// the snapshot.
|
||||
///
|
||||
/// Defaults to false.
|
||||
final bool autoresize;
|
||||
|
||||
/// The painter used to paint the child snapshot or child widgets.
|
||||
final SnapshotPainter painter;
|
||||
|
||||
@ -136,6 +143,7 @@ class SnapshotWidget extends SingleChildRenderObjectWidget {
|
||||
mode: mode,
|
||||
devicePixelRatio: MediaQuery.of(context).devicePixelRatio,
|
||||
painter: painter,
|
||||
autoresize: autoresize,
|
||||
);
|
||||
}
|
||||
|
||||
@ -146,7 +154,8 @@ class SnapshotWidget extends SingleChildRenderObjectWidget {
|
||||
..controller = controller
|
||||
..mode = mode
|
||||
..devicePixelRatio = MediaQuery.of(context).devicePixelRatio
|
||||
..painter = painter;
|
||||
..painter = painter
|
||||
..autoresize = autoresize;
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,10 +168,12 @@ class _RenderSnapshotWidget extends RenderProxyBox {
|
||||
required SnapshotController controller,
|
||||
required SnapshotMode mode,
|
||||
required SnapshotPainter painter,
|
||||
required bool autoresize,
|
||||
}) : _devicePixelRatio = devicePixelRatio,
|
||||
_controller = controller,
|
||||
_mode = mode,
|
||||
_painter = painter;
|
||||
_painter = painter,
|
||||
_autoresize = autoresize;
|
||||
|
||||
/// The device pixel ratio used to create the child image.
|
||||
double get devicePixelRatio => _devicePixelRatio;
|
||||
@ -230,6 +241,17 @@ class _RenderSnapshotWidget extends RenderProxyBox {
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
/// Whether or not changes in render object size should automatically re-rasterize.
|
||||
bool get autoresize => _autoresize;
|
||||
bool _autoresize;
|
||||
set autoresize(bool value) {
|
||||
if (value == autoresize) {
|
||||
return;
|
||||
}
|
||||
_autoresize = value;
|
||||
markNeedsPaint();
|
||||
}
|
||||
|
||||
ui.Image? _childRaster;
|
||||
Size? _childRasterSize;
|
||||
// Set to true if the snapshot mode was not forced and a platform view
|
||||
@ -292,9 +314,12 @@ class _RenderSnapshotWidget extends RenderProxyBox {
|
||||
}
|
||||
final ui.Image image = offsetLayer.toImageSync(Offset.zero & size, pixelRatio: devicePixelRatio);
|
||||
offsetLayer.dispose();
|
||||
_lastCachedSize = size;
|
||||
return image;
|
||||
}
|
||||
|
||||
Size? _lastCachedSize;
|
||||
|
||||
@override
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
if (size.isEmpty) {
|
||||
@ -310,6 +335,12 @@ class _RenderSnapshotWidget extends RenderProxyBox {
|
||||
painter.paint(context, offset, size, super.paint);
|
||||
return;
|
||||
}
|
||||
|
||||
if (autoresize && size != _lastCachedSize && _lastCachedSize != null) {
|
||||
_childRaster?.dispose();
|
||||
_childRaster = null;
|
||||
}
|
||||
|
||||
if (_childRaster == null) {
|
||||
_childRaster = _paintAndDetachToImage();
|
||||
_childRasterSize = size * devicePixelRatio;
|
||||
|
@ -3,6 +3,8 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
@Tags(<String>['reduced-test-set'])
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/cupertino.dart' show CupertinoPageRoute;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
@ -236,13 +238,17 @@ void main() {
|
||||
expect(find.text('Page 2'), findsNothing);
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
|
||||
|
||||
testWidgets('test page transition (_ZoomPageTransition) with rasterization re-rasterizes when window size changes', (WidgetTester tester) async {
|
||||
// Shrink the window size.
|
||||
testWidgets('test page transition (_ZoomPageTransition) with rasterization re-rasterizes when window insets', (WidgetTester tester) async {
|
||||
late Size oldSize;
|
||||
late ui.WindowPadding oldInsets;
|
||||
try {
|
||||
oldSize = tester.binding.window.physicalSize;
|
||||
oldInsets = tester.binding.window.viewInsets;
|
||||
tester.binding.window.physicalSizeTestValue = const Size(1000, 1000);
|
||||
tester.binding.window.viewInsetsTestValue = ui.WindowPadding.zero;
|
||||
|
||||
// Intentionally use nested scaffolds to simulate the view insets being
|
||||
// consumed.
|
||||
final Key key = GlobalKey();
|
||||
await tester.pumpWidget(
|
||||
RepaintBoundary(
|
||||
@ -251,7 +257,9 @@ void main() {
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (BuildContext context) {
|
||||
return const Material(child: SizedBox.shrink());
|
||||
return const Scaffold(body: Scaffold(
|
||||
body: Material(child: SizedBox.shrink())
|
||||
));
|
||||
},
|
||||
);
|
||||
},
|
||||
@ -265,8 +273,8 @@ void main() {
|
||||
|
||||
await expectLater(find.byKey(key), matchesGoldenFile('zoom_page_transition.small.png'));
|
||||
|
||||
// Increase the window size.
|
||||
tester.binding.window.physicalSizeTestValue = const Size(1000, 2000);
|
||||
// Change the view insets
|
||||
tester.binding.window.viewInsetsTestValue = const TestWindowPadding(left: 0, top: 0, right: 0, bottom: 500);
|
||||
|
||||
await tester.pump();
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
@ -274,6 +282,7 @@ void main() {
|
||||
await expectLater(find.byKey(key), matchesGoldenFile('zoom_page_transition.big.png'));
|
||||
} finally {
|
||||
tester.binding.window.physicalSizeTestValue = oldSize;
|
||||
tester.binding.window.viewInsetsTestValue = oldInsets;
|
||||
}
|
||||
}, variant: TargetPlatformVariant.only(TargetPlatform.android), skip: kIsWeb); // [intended] rasterization is not used on the web.
|
||||
|
||||
@ -1236,3 +1245,21 @@ class TestDependencies extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class TestWindowPadding implements ui.WindowPadding {
|
||||
const TestWindowPadding({
|
||||
required this.left,
|
||||
required this.top,
|
||||
required this.right,
|
||||
required this.bottom,
|
||||
});
|
||||
|
||||
@override
|
||||
final double left;
|
||||
@override
|
||||
final double top;
|
||||
@override
|
||||
final double right;
|
||||
@override
|
||||
final double bottom;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user