Skip to content

Commit e065c7f

Browse files
[framework] make ImageFiltered a repaint boundary (#116385)
* ++ * ++ * ++
1 parent 5c97543 commit e065c7f

File tree

3 files changed

+75
-22
lines changed

3 files changed

+75
-22
lines changed

packages/flutter/lib/src/widgets/image_filter.dart

+12-20
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ class _ImageFilterRenderObject extends RenderProxyBox {
7979
if (enabled == value) {
8080
return;
8181
}
82+
final bool wasRepaintBoundary = isRepaintBoundary;
8283
_enabled = value;
84+
if (isRepaintBoundary != wasRepaintBoundary) {
85+
markNeedsCompositingBitsUpdate();
86+
}
8387
markNeedsPaint();
8488
}
8589

@@ -89,32 +93,20 @@ class _ImageFilterRenderObject extends RenderProxyBox {
8993
assert(value != null);
9094
if (value != _imageFilter) {
9195
_imageFilter = value;
92-
markNeedsPaint();
96+
markNeedsCompositedLayerUpdate();
9397
}
9498
}
9599

96100
@override
97101
bool get alwaysNeedsCompositing => child != null && enabled;
98102

99-
@override
100-
void paint(PaintingContext context, Offset offset) {
101-
assert(imageFilter != null);
102-
if (!enabled) {
103-
layer = null;
104-
return super.paint(context, offset);
105-
}
103+
@override
104+
bool get isRepaintBoundary => alwaysNeedsCompositing;
106105

107-
if (layer == null) {
108-
layer = ImageFilterLayer(imageFilter: imageFilter, offset: offset);
109-
} else {
110-
final ImageFilterLayer filterLayer = layer! as ImageFilterLayer;
111-
filterLayer.imageFilter = imageFilter;
112-
filterLayer.offset = offset;
113-
}
114-
context.pushLayer(layer!, super.paint, Offset.zero);
115-
assert(() {
116-
layer!.debugCreator = debugCreator;
117-
return true;
118-
}());
106+
@override
107+
OffsetLayer updateCompositedLayer({required covariant ImageFilterLayer? oldLayer}) {
108+
final ImageFilterLayer layer = oldLayer ?? ImageFilterLayer();
109+
layer.imageFilter = imageFilter;
110+
return layer;
119111
}
120112
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2014 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:ui';
6+
7+
import 'package:flutter/material.dart';
8+
import 'package:flutter/rendering.dart';
9+
import 'package:flutter_test/flutter_test.dart';
10+
11+
void main() {
12+
testWidgets('ImageFiltered avoids repainting child as it animates', (WidgetTester tester) async {
13+
RenderTestObject.paintCount = 0;
14+
await tester.pumpWidget(
15+
Container(
16+
color: Colors.red,
17+
child: ImageFiltered(
18+
imageFilter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
19+
child: const TestWidget(),
20+
),
21+
)
22+
);
23+
24+
expect(RenderTestObject.paintCount, 1);
25+
26+
await tester.pumpWidget(
27+
Container(
28+
color: Colors.red,
29+
child: ImageFiltered(
30+
imageFilter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
31+
child: const TestWidget(),
32+
),
33+
)
34+
);
35+
36+
expect(RenderTestObject.paintCount, 1);
37+
});
38+
}
39+
40+
class TestWidget extends SingleChildRenderObjectWidget {
41+
const TestWidget({super.key, super.child});
42+
43+
@override
44+
RenderObject createRenderObject(BuildContext context) {
45+
return RenderTestObject();
46+
}
47+
}
48+
49+
class RenderTestObject extends RenderProxyBox {
50+
static int paintCount = 0;
51+
52+
@override
53+
void paint(PaintingContext context, Offset offset) {
54+
paintCount += 1;
55+
super.paint(context, offset);
56+
}
57+
}

packages/flutter/test/widgets/image_filter_test.dart

+6-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ void main() {
3131
});
3232

3333
testWidgets('Image filter - blur with offset', (WidgetTester tester) async {
34+
final Key key = GlobalKey();
3435
await tester.pumpWidget(
3536
RepaintBoundary(
37+
key: key,
3638
child: Transform.translate(
3739
offset: const Offset(50, 50),
3840
child: ImageFiltered(
@@ -43,7 +45,7 @@ void main() {
4345
),
4446
);
4547
await expectLater(
46-
find.byType(ImageFiltered),
48+
find.byKey(key),
4749
matchesGoldenFile('image_filter_blur_offset.png'),
4850
);
4951
});
@@ -119,8 +121,10 @@ void main() {
119121
testWidgets('Image filter - matrix with offset', (WidgetTester tester) async {
120122
final Matrix4 matrix = Matrix4.rotationZ(pi / 18);
121123
final ImageFilter matrixFilter = ImageFilter.matrix(matrix.storage);
124+
final Key key = GlobalKey();
122125
await tester.pumpWidget(
123126
RepaintBoundary(
127+
key: key,
124128
child: Transform.translate(
125129
offset: const Offset(50, 50),
126130
child: ImageFiltered(
@@ -147,7 +151,7 @@ void main() {
147151
),
148152
);
149153
await expectLater(
150-
find.byType(ImageFiltered),
154+
find.byKey(key),
151155
matchesGoldenFile('image_filter_matrix_offset.png'),
152156
);
153157
});

0 commit comments

Comments
 (0)