mirror of
https://github.com/flutter/flutter
synced 2024-08-25 02:46:31 +00:00
Layout animated GIFs only once (#143188)
Fixes https://github.com/flutter/flutter/issues/138610. When `RenderImage` receives a new `Image` it only needs to fire up the layout machinery when the dimensions of the image have changed compared to the previous image. If the dimensions are the same, a repaint is sufficient.
This commit is contained in:
parent
5f1a3c16bd
commit
0aa9b5e17d
|
@ -93,10 +93,11 @@ class RenderImage extends RenderBox {
|
|||
value.dispose();
|
||||
return;
|
||||
}
|
||||
final bool sizeChanged = _image?.width != value?.width || _image?.height != value?.height;
|
||||
_image?.dispose();
|
||||
_image = value;
|
||||
markNeedsPaint();
|
||||
if (_width == null || _height == null) {
|
||||
if (sizeChanged && (_width == null || _height == null)) {
|
||||
markNeedsLayout();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2065,6 +2065,61 @@ void main() {
|
|||
: isNot(throwsA(anything)),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('Animated GIFs do not require layout for subsequent frames', (WidgetTester tester) async {
|
||||
final ui.Codec codec = (await tester.runAsync(() {
|
||||
return ui.instantiateImageCodec(Uint8List.fromList(kAnimatedGif));
|
||||
}))!;
|
||||
|
||||
Future<ui.Image> nextFrame() async {
|
||||
final ui.FrameInfo frameInfo = (await tester.runAsync(codec.getNextFrame))!;
|
||||
return frameInfo.image;
|
||||
}
|
||||
|
||||
final _TestImageStreamCompleter streamCompleter = _TestImageStreamCompleter();
|
||||
final _TestImageProvider imageProvider = _TestImageProvider(streamCompleter: streamCompleter);
|
||||
int? lastFrame;
|
||||
|
||||
await tester.pumpWidget(
|
||||
Center(
|
||||
child: Image(
|
||||
image: imageProvider,
|
||||
frameBuilder: (BuildContext context, Widget child, int? frame, bool wasSynchronouslyLoaded) {
|
||||
lastFrame = frame;
|
||||
return child;
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.getSize(find.byType(Image)), Size.zero);
|
||||
|
||||
streamCompleter.setData(imageInfo: ImageInfo(image: await nextFrame()));
|
||||
await tester.pump();
|
||||
expect(lastFrame, 0);
|
||||
expect(tester.allRenderObjects.whereType<RenderImage>().single.debugNeedsLayout, isFalse);
|
||||
expect(tester.allRenderObjects.whereType<RenderImage>().single.debugNeedsPaint, isFalse);
|
||||
expect(tester.getSize(find.byType(Image)), const Size(1, 1));
|
||||
|
||||
streamCompleter.setData(imageInfo: ImageInfo(image: await nextFrame()));
|
||||
// We only complete the build phase and expect that it does not mark the
|
||||
// RenderImage for layout because the new frame has the same dimensions as
|
||||
// the old one. We only need to repaint.
|
||||
await tester.pump(null, EnginePhase.build);
|
||||
expect(lastFrame, 1);
|
||||
expect(tester.allRenderObjects.whereType<RenderImage>().single.debugNeedsLayout, isFalse);
|
||||
expect(tester.allRenderObjects.whereType<RenderImage>().single.debugNeedsPaint, isTrue);
|
||||
expect(tester.getSize(find.byType(Image)), const Size(1, 1));
|
||||
|
||||
streamCompleter.setData(imageInfo: ImageInfo(image: await nextFrame()));
|
||||
await tester.pump();
|
||||
expect(lastFrame, 2);
|
||||
expect(tester.allRenderObjects.whereType<RenderImage>().single.debugNeedsLayout, isFalse);
|
||||
expect(tester.allRenderObjects.whereType<RenderImage>().single.debugNeedsPaint, isFalse);
|
||||
expect(tester.getSize(find.byType(Image)), const Size(1, 1));
|
||||
|
||||
codec.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
@immutable
|
||||
|
|
Loading…
Reference in a new issue