Skip to content

Commit 1cc9547

Browse files
authored
fix: mask sizing on scaling transform widget children (#2520)
* fix: mask sizing on scaling transform widget children * chore: changelog
1 parent ec83631 commit 1cc9547

File tree

5 files changed

+35
-45
lines changed

5 files changed

+35
-45
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
### Fixes
1010

1111
- Missing replay gestures on Android ([#2515](https://github.com/getsentry/sentry-dart/pull/2515))
12+
- Replay mask sizing on scaling transform widget children ([#2520](https://github.com/getsentry/sentry-dart/pull/2520))
1213

1314
### Enhancements
1415

flutter/lib/src/screenshot/recorder.dart

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,10 +122,9 @@ class ScreenshotRecorder {
122122
final filter = WidgetFilter(_maskingConfig!, options.logger);
123123
final colorScheme = capture.context.findColorScheme();
124124
filter.obscure(
125+
root: capture.root,
125126
context: capture.context,
126-
pixelRatio: capture.pixelRatio,
127127
colorScheme: colorScheme,
128-
bounds: capture.bounds,
129128
);
130129
return filter.items;
131130
}
@@ -134,14 +133,16 @@ class ScreenshotRecorder {
134133
}
135134

136135
class _Capture<R> {
136+
final RenderRepaintBoundary root;
137137
final widgets.BuildContext context;
138138
final double srcWidth;
139139
final double srcHeight;
140140
final double pixelRatio;
141141
final _completer = Completer<R>();
142142

143143
_Capture._(
144-
{required this.context,
144+
{required this.root,
145+
required this.context,
145146
required this.srcWidth,
146147
required this.srcHeight,
147148
required this.pixelRatio});
@@ -158,16 +159,14 @@ class _Capture<R> {
158159
widgets.MediaQuery.of(context).devicePixelRatio;
159160

160161
return _Capture._(
162+
root: renderObject,
161163
context: context,
162164
srcWidth: srcWidth,
163165
srcHeight: srcHeight,
164166
pixelRatio: pixelRatio,
165167
);
166168
}
167169

168-
Rect get bounds =>
169-
Rect.fromLTWH(0, 0, srcWidth * pixelRatio, srcHeight * pixelRatio);
170-
171170
int get width => (srcWidth * pixelRatio).round();
172171

173172
int get height => (srcHeight * pixelRatio).round();
@@ -226,7 +225,13 @@ class _Capture<R> {
226225
final paint = Paint()..style = PaintingStyle.fill;
227226
for (var item in items) {
228227
paint.color = item.color;
229-
canvas.drawRect(item.bounds, paint);
228+
final source = item.bounds;
229+
final scaled = Rect.fromLTRB(
230+
source.left * pixelRatio,
231+
source.top * pixelRatio,
232+
source.right * pixelRatio,
233+
source.bottom * pixelRatio);
234+
canvas.drawRect(scaled, paint);
230235
}
231236
}
232237
}

flutter/lib/src/screenshot/widget_filter.dart

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class WidgetFilter {
1212
final SentryLogger logger;
1313
final SentryMaskingConfig config;
1414
late WidgetFilterColorScheme _scheme;
15-
late double _pixelRatio;
15+
late RenderObject _root;
1616
late Rect _bounds;
1717
late List<Element> _visitList;
1818
final _warnedWidgets = <int>{};
@@ -24,14 +24,14 @@ class WidgetFilter {
2424
WidgetFilter(this.config, this.logger);
2525

2626
void obscure({
27+
required RenderRepaintBoundary root,
2728
required BuildContext context,
28-
required double pixelRatio,
29-
required Rect bounds,
3029
required WidgetFilterColorScheme colorScheme,
30+
Rect? bounds,
3131
}) {
32-
_pixelRatio = pixelRatio;
33-
_bounds = bounds;
32+
_root = root;
3433
_scheme = colorScheme;
34+
_bounds = bounds ?? Offset.zero & root.size;
3535
assert(colorScheme.background.isOpaque);
3636
assert(colorScheme.defaultMask.isOpaque);
3737
assert(colorScheme.defaultTextMask.isOpaque);
@@ -215,13 +215,8 @@ class WidgetFilter {
215215

216216
@pragma('vm:prefer-inline')
217217
Rect _boundingBox(RenderBox box) {
218-
final offset = box.localToGlobal(Offset.zero);
219-
return Rect.fromLTWH(
220-
offset.dx * _pixelRatio,
221-
offset.dy * _pixelRatio,
222-
box.size.width * _pixelRatio,
223-
box.size.height * _pixelRatio,
224-
);
218+
final transform = box.getTransformTo(_root);
219+
return MatrixUtils.transformRect(transform, box.paintBounds);
225220
}
226221
}
227222

flutter/test/screenshot/test_widget.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Future<Element> pumpTestElement(WidgetTester tester,
5959
),
6060
),
6161
);
62-
return TestWidgetsFlutterBinding.instance.rootElement!;
62+
return find.byType(SentryScreenshotWidget).evaluate().first;
6363
}
6464

6565
final testImageData = Uint8List.fromList([

flutter/test/screenshot/widget_filter_test.dart

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter/rendering.dart';
23
import 'package:flutter/services.dart';
34
import 'package:flutter_test/flutter_test.dart';
45
import 'package:sentry_flutter/sentry_flutter.dart';
@@ -12,7 +13,6 @@ import 'test_widget.dart';
1213
// We'll keep these tests although they're not unit-tests anymore.
1314
void main() async {
1415
TestWidgetsFlutterBinding.ensureInitialized();
15-
const defaultBounds = Rect.fromLTRB(0, 0, 1000, 1000);
1616
final rootBundle = TestAssetBundle();
1717
final otherBundle = TestAssetBundle();
1818
final logger = MockLogger();
@@ -43,8 +43,7 @@ void main() async {
4343
final element = await pumpTestElement(tester);
4444
sut.obscure(
4545
context: element,
46-
pixelRatio: 1.0,
47-
bounds: defaultBounds,
46+
root: element.renderObject as RenderRepaintBoundary,
4847
colorScheme: colorScheme);
4948
expect(sut.items.length, 6);
5049
});
@@ -54,8 +53,7 @@ void main() async {
5453
final element = await pumpTestElement(tester);
5554
sut.obscure(
5655
context: element,
57-
pixelRatio: 1.0,
58-
bounds: defaultBounds,
56+
root: element.renderObject as RenderRepaintBoundary,
5957
colorScheme: colorScheme);
6058
expect(sut.items.length, 0);
6159
});
@@ -66,7 +64,7 @@ void main() async {
6664
final element = await pumpTestElement(tester);
6765
sut.obscure(
6866
context: element,
69-
pixelRatio: 1.0,
67+
root: element.renderObject as RenderRepaintBoundary,
7068
bounds: Rect.fromLTRB(0, 0, 100, 100),
7169
colorScheme: colorScheme);
7270
expect(sut.items.length, 1);
@@ -77,8 +75,7 @@ void main() async {
7775
final element = await pumpTestElement(tester);
7876
sut.obscure(
7977
context: element,
80-
pixelRatio: 1.0,
81-
bounds: defaultBounds,
78+
root: element.renderObject as RenderRepaintBoundary,
8279
colorScheme: colorScheme);
8380
expect(sut.items.length, 6);
8481
expect(
@@ -100,8 +97,7 @@ void main() async {
10097
final element = await pumpTestElement(tester);
10198
sut.obscure(
10299
context: element,
103-
pixelRatio: 1.0,
104-
bounds: defaultBounds,
100+
root: element.renderObject as RenderRepaintBoundary,
105101
colorScheme: colorScheme);
106102
expect(sut.items.length, 3);
107103
});
@@ -144,8 +140,7 @@ void main() async {
144140
final element = await pumpTestElement(tester);
145141
sut.obscure(
146142
context: element,
147-
pixelRatio: 1.0,
148-
bounds: defaultBounds,
143+
root: element.renderObject as RenderRepaintBoundary,
149144
colorScheme: colorScheme);
150145
expect(sut.items.length, 0);
151146
});
@@ -156,7 +151,7 @@ void main() async {
156151
final element = await pumpTestElement(tester);
157152
sut.obscure(
158153
context: element,
159-
pixelRatio: 1.0,
154+
root: element.renderObject as RenderRepaintBoundary,
160155
bounds: Rect.fromLTRB(0, 0, 500, 100),
161156
colorScheme: colorScheme);
162157
expect(sut.items.length, 1);
@@ -167,8 +162,7 @@ void main() async {
167162
final element = await pumpTestElement(tester);
168163
sut.obscure(
169164
context: element,
170-
pixelRatio: 1.0,
171-
bounds: defaultBounds,
165+
root: element.renderObject as RenderRepaintBoundary,
172166
colorScheme: colorScheme);
173167
expect(sut.items.length, 3);
174168
expect(boundsRect(sut.items[0]), '1x1');
@@ -184,8 +178,7 @@ void main() async {
184178
]);
185179
sut.obscure(
186180
context: element,
187-
pixelRatio: 1.0,
188-
bounds: defaultBounds,
181+
root: element.renderObject as RenderRepaintBoundary,
189182
colorScheme: colorScheme);
190183
expect(sut.items.length, 1);
191184
expect(boundsRect(sut.items[0]), '344x248');
@@ -200,8 +193,7 @@ void main() async {
200193
]);
201194
sut.obscure(
202195
context: element,
203-
pixelRatio: 1.0,
204-
bounds: defaultBounds,
196+
root: element.renderObject as RenderRepaintBoundary,
205197
colorScheme: colorScheme);
206198
expect(sut.items, isEmpty);
207199
});
@@ -213,16 +205,14 @@ void main() async {
213205
]);
214206
sut.obscure(
215207
context: element,
216-
pixelRatio: 1.0,
217-
bounds: defaultBounds,
208+
root: element.renderObject as RenderRepaintBoundary,
218209
colorScheme: colorScheme);
219210
expect(sut.items.length, 1);
220211
expect(boundsRect(sut.items[0]), '144x48');
221212
sut.throwInObscure = true;
222213
sut.obscure(
223214
context: element,
224-
pixelRatio: 1.0,
225-
bounds: defaultBounds,
215+
root: element.renderObject as RenderRepaintBoundary,
226216
colorScheme: colorScheme);
227217
expect(sut.items.length, 1);
228218
expect(boundsRect(sut.items[0]), '344x248');
@@ -239,8 +229,7 @@ void main() async {
239229
await pumpTestElement(tester, children: [CustomPasswordWidget()]);
240230
sut.obscure(
241231
context: element,
242-
pixelRatio: 1.0,
243-
bounds: defaultBounds,
232+
root: element.renderObject as RenderRepaintBoundary,
244233
colorScheme: colorScheme);
245234
final logMessages = logger.items
246235
.where((item) => item.level == SentryLevel.warning)

0 commit comments

Comments
 (0)