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/foundation.dart'; |
8 | import 'package:flutter/rendering.dart'; |
9 | |
10 | import 'framework.dart'; |
11 | |
12 | /// Applies an [ImageFilter] to its child. |
13 | /// |
14 | /// An image filter will always apply its filter operation to the child widget, |
15 | /// even if said filter is conceptually a "no-op", such as an ImageFilter.blur |
16 | /// with a radius of 0 or an ImageFilter.matrix with an identity matrix. Setting |
17 | /// [ImageFiltered.enabled] to `false` is a more efficient manner of disabling |
18 | /// an image filter. |
19 | /// |
20 | /// The framework does not attempt to optimize out "no-op" filters because it |
21 | /// cannot tell the difference between an intentional no-op and a filter that is |
22 | /// only incidentally a no-op. Consider an ImageFilter.matrix that is animated |
23 | /// and happens to pass through the identity matrix. If the framework identified it |
24 | /// as a no-op it would drop and then recreate the layer during the animation which |
25 | /// would be more expensive than keeping it around. |
26 | /// |
27 | /// {@youtube 560 315 https://www.youtube.com/watch?v=7Lftorq4i2o} |
28 | /// |
29 | /// See also: |
30 | /// |
31 | /// * [BackdropFilter], which applies an [ImageFilter] to everything |
32 | /// beneath its child. |
33 | /// * [ColorFiltered], which applies a [ColorFilter] to its child. |
34 | @immutable |
35 | class ImageFiltered extends SingleChildRenderObjectWidget { |
36 | /// Creates a widget that applies an [ImageFilter] to its child. |
37 | const ImageFiltered({ |
38 | super.key, |
39 | required this.imageFilter, |
40 | super.child, |
41 | this.enabled = true, |
42 | }); |
43 | |
44 | /// The image filter to apply to the child of this widget. |
45 | final ImageFilter imageFilter; |
46 | |
47 | /// Whether or not to apply the image filter operation to the child of this |
48 | /// widget. |
49 | /// |
50 | /// Prefer setting enabled to `false` instead of creating a "no-op" filter |
51 | /// type for performance reasons. |
52 | final bool enabled; |
53 | |
54 | @override |
55 | RenderObject createRenderObject(BuildContext context) => _ImageFilterRenderObject(imageFilter, enabled); |
56 | |
57 | @override |
58 | void updateRenderObject(BuildContext context, RenderObject renderObject) { |
59 | (renderObject as _ImageFilterRenderObject) |
60 | ..enabled = enabled |
61 | ..imageFilter = imageFilter; |
62 | } |
63 | |
64 | @override |
65 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
66 | super.debugFillProperties(properties); |
67 | properties.add(DiagnosticsProperty<ImageFilter>('imageFilter' , imageFilter)); |
68 | } |
69 | } |
70 | |
71 | class _ImageFilterRenderObject extends RenderProxyBox { |
72 | _ImageFilterRenderObject(this._imageFilter, this._enabled); |
73 | |
74 | bool get enabled => _enabled; |
75 | bool _enabled; |
76 | set enabled(bool value) { |
77 | if (enabled == value) { |
78 | return; |
79 | } |
80 | final bool wasRepaintBoundary = isRepaintBoundary; |
81 | _enabled = value; |
82 | if (isRepaintBoundary != wasRepaintBoundary) { |
83 | markNeedsCompositingBitsUpdate(); |
84 | } |
85 | markNeedsPaint(); |
86 | } |
87 | |
88 | ImageFilter get imageFilter => _imageFilter; |
89 | ImageFilter _imageFilter; |
90 | set imageFilter(ImageFilter value) { |
91 | if (value != _imageFilter) { |
92 | _imageFilter = value; |
93 | markNeedsCompositedLayerUpdate(); |
94 | } |
95 | } |
96 | |
97 | @override |
98 | bool get alwaysNeedsCompositing => child != null && enabled; |
99 | |
100 | @override |
101 | bool get isRepaintBoundary => alwaysNeedsCompositing; |
102 | |
103 | @override |
104 | OffsetLayer updateCompositedLayer({required covariant ImageFilterLayer? oldLayer}) { |
105 | final ImageFilterLayer layer = oldLayer ?? ImageFilterLayer(); |
106 | layer.imageFilter = imageFilter; |
107 | return layer; |
108 | } |
109 | } |
110 | |