Skip to content

Commit 7108049

Browse files
committed
[EFFECTS] Background blur works with arbitrary shapes
1 parent 88a6582 commit 7108049

File tree

2 files changed

+75
-6
lines changed

2 files changed

+75
-6
lines changed

package/pheno_ui/lib/models/figma_effects_model.dart

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import 'dart:ui';
22

33
import 'package:flutter/widgets.dart';
44

5-
import '../interface/log.dart';
65
import '../widgets/inner_shadow.dart';
76
import '../widgets/outer_shadow.dart';
7+
import '../widgets/background_blur.dart';
88

99
class FigmaEffectsModel {
1010
final List<_FigmaEffect> _effects;
@@ -218,11 +218,9 @@ class _FigmaBackgroundBlur extends _FigmaEffect {
218218
Widget apply(Widget child) {
219219
if (!visible) return child;
220220

221-
return ClipRect(
222-
child: BackdropFilter(
223-
filter: ImageFilter.blur(sigmaX: radius * 0.5, sigmaY: radius * 0.5),
224-
child: child,
225-
),
221+
return BackgroundBlur(
222+
radius: radius,
223+
child: child,
226224
);
227225
}
228226
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import 'dart:ui';
2+
import 'package:flutter/rendering.dart';
3+
import 'package:flutter/widgets.dart';
4+
5+
class BackgroundBlur extends SingleChildRenderObjectWidget {
6+
final double radius;
7+
8+
const BackgroundBlur({
9+
required this.radius,
10+
super.child,
11+
super.key,
12+
});
13+
14+
@override
15+
RenderObject createRenderObject(BuildContext context) {
16+
final renderObject = RenderBackgroundBlur();
17+
updateRenderObject(context, renderObject);
18+
return renderObject;
19+
}
20+
21+
@override
22+
void updateRenderObject(BuildContext context, RenderBackgroundBlur renderObject) {
23+
renderObject.radius = radius;
24+
}
25+
}
26+
27+
class RenderBackgroundBlur extends RenderProxyBox {
28+
late double radius;
29+
30+
@override
31+
void paint(PaintingContext context, Offset offset) {
32+
if (child == null) return;
33+
final bounds = offset & size;
34+
35+
const forceWhite = ColorFilter.matrix(<double>[
36+
255, 0, 0, 0, 255,
37+
0, 255, 0, 0, 255,
38+
0, 0, 255, 0, 255,
39+
0, 0, 0, 255, 0,
40+
]);
41+
42+
final maskPaint = Paint()
43+
..color = const Color(0xFFFFFFFF)
44+
..blendMode = BlendMode.dstIn
45+
..imageFilter = ImageFilter.compose(
46+
inner: forceWhite,
47+
outer: ImageFilter.blur(sigmaX: 1, sigmaY: 1),
48+
)
49+
;
50+
51+
BackdropFilterLayer backdropFilterLayer = BackdropFilterLayer(
52+
filter: ImageFilter.blur(sigmaX: radius * 0.5, sigmaY: radius * 0.5),
53+
);
54+
55+
// clip the image to optimize multiple backdrop filters on screen:
56+
// https://github.com/flutter/flutter/issues/126353
57+
context.pushClipRect(true, offset, Offset.zero & size, (context, offset) {
58+
// push the backdrop filter
59+
context.pushLayer(backdropFilterLayer, (context, offset) {
60+
// mask the effect based on the children being rendered
61+
context.canvas.saveLayer(bounds, maskPaint);
62+
context.paintChild(child!, offset);
63+
context.canvas.restore();
64+
}, offset);
65+
});
66+
67+
// paint the original child on top of the effect
68+
// NOTE: The children must be translucent for the effect to be visible
69+
context.paintChild(child!, offset);
70+
}
71+
}

0 commit comments

Comments
 (0)