[framework] allow disabling image filter layer (#102085)
This commit is contained in:
parent
0411a6f8a3
commit
f15295cc91
@ -11,6 +11,19 @@ import 'framework.dart';
|
|||||||
|
|
||||||
/// Applies an [ImageFilter] to its child.
|
/// Applies an [ImageFilter] to its child.
|
||||||
///
|
///
|
||||||
|
/// An image filter will always apply its filter operation to the child widget,
|
||||||
|
/// even if said filter is conceptually a "no-op", such as an ImageFilter.blur
|
||||||
|
/// with a radius of 0 or an ImageFilter.matrix with an identity matrix. Setting
|
||||||
|
/// [ImageFiltered.enabled] to `false` is a more efficient manner of disabling
|
||||||
|
/// an image filter.
|
||||||
|
///
|
||||||
|
/// The framework does not attempt to optimize out "no-op" filters because it
|
||||||
|
/// cannot tell the difference between an intentional no-op and a filter that is
|
||||||
|
/// only incidentally a no-op. Consider an ImageFilter.matrix that is animated
|
||||||
|
/// and happens to pass through the identity matrix. If the framework identified it
|
||||||
|
/// as a no-op it would drop and then recreate the layer during the animation which
|
||||||
|
/// would be more expensive than keeping it around.
|
||||||
|
///
|
||||||
/// {@youtube 560 315 https://www.youtube.com/watch?v=7Lftorq4i2o}
|
/// {@youtube 560 315 https://www.youtube.com/watch?v=7Lftorq4i2o}
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
@ -27,17 +40,27 @@ class ImageFiltered extends SingleChildRenderObjectWidget {
|
|||||||
super.key,
|
super.key,
|
||||||
required this.imageFilter,
|
required this.imageFilter,
|
||||||
super.child,
|
super.child,
|
||||||
|
this.enabled = true,
|
||||||
}) : assert(imageFilter != null);
|
}) : assert(imageFilter != null);
|
||||||
|
|
||||||
/// The image filter to apply to the child of this widget.
|
/// The image filter to apply to the child of this widget.
|
||||||
final ImageFilter imageFilter;
|
final ImageFilter imageFilter;
|
||||||
|
|
||||||
|
/// Whether or not to apply the image filter opation to the child of this
|
||||||
|
/// widget.
|
||||||
|
///
|
||||||
|
/// Prefer setting enabled to `false` instead of creating a "no-op" filter
|
||||||
|
/// type for performance reasons.
|
||||||
|
final bool enabled;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
RenderObject createRenderObject(BuildContext context) => _ImageFilterRenderObject(imageFilter);
|
RenderObject createRenderObject(BuildContext context) => _ImageFilterRenderObject(imageFilter, enabled);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateRenderObject(BuildContext context, RenderObject renderObject) {
|
void updateRenderObject(BuildContext context, RenderObject renderObject) {
|
||||||
(renderObject as _ImageFilterRenderObject).imageFilter = imageFilter;
|
(renderObject as _ImageFilterRenderObject)
|
||||||
|
..enabled = enabled
|
||||||
|
..imageFilter = imageFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -48,7 +71,17 @@ class ImageFiltered extends SingleChildRenderObjectWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ImageFilterRenderObject extends RenderProxyBox {
|
class _ImageFilterRenderObject extends RenderProxyBox {
|
||||||
_ImageFilterRenderObject(this._imageFilter);
|
_ImageFilterRenderObject(this._imageFilter, this._enabled);
|
||||||
|
|
||||||
|
bool get enabled => _enabled;
|
||||||
|
bool _enabled;
|
||||||
|
set enabled(bool value) {
|
||||||
|
if (enabled == value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_enabled = value;
|
||||||
|
markNeedsPaint();
|
||||||
|
}
|
||||||
|
|
||||||
ImageFilter get imageFilter => _imageFilter;
|
ImageFilter get imageFilter => _imageFilter;
|
||||||
ImageFilter _imageFilter;
|
ImageFilter _imageFilter;
|
||||||
@ -61,11 +94,16 @@ class _ImageFilterRenderObject extends RenderProxyBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get alwaysNeedsCompositing => child != null;
|
bool get alwaysNeedsCompositing => child != null && enabled;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void paint(PaintingContext context, Offset offset) {
|
void paint(PaintingContext context, Offset offset) {
|
||||||
assert(imageFilter != null);
|
assert(imageFilter != null);
|
||||||
|
if (!enabled) {
|
||||||
|
layer = null;
|
||||||
|
return super.paint(context, offset);
|
||||||
|
}
|
||||||
|
|
||||||
if (layer == null) {
|
if (layer == null) {
|
||||||
layer = ImageFilterLayer(imageFilter: imageFilter);
|
layer = ImageFilterLayer(imageFilter: imageFilter);
|
||||||
} else {
|
} else {
|
||||||
|
@ -86,4 +86,25 @@ void main() {
|
|||||||
await pumpWithSigma(10.0);
|
await pumpWithSigma(10.0);
|
||||||
expect(renderObject.debugLayer, same(originalLayer));
|
expect(renderObject.debugLayer, same(originalLayer));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Image filter - enabled and disabled', (WidgetTester tester) async {
|
||||||
|
Future<void> pumpWithEnabledStaet(bool enabled) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
RepaintBoundary(
|
||||||
|
child: ImageFiltered(
|
||||||
|
enabled: enabled,
|
||||||
|
imageFilter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
|
||||||
|
child: const Placeholder(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await pumpWithEnabledStaet(false);
|
||||||
|
expect(tester.layers, isNot(contains(isA<ImageFilterLayer>())));
|
||||||
|
|
||||||
|
|
||||||
|
await pumpWithEnabledStaet(true);
|
||||||
|
expect(tester.layers, contains(isA<ImageFilterLayer>()));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user