collectReplayMaskRects static method
Pixel rects of every auto-masked widget plus every PlotlineSensitive, for session replay masking.
Implementation
static List<Map<String, int>> collectReplayMaskRects(double pixRatio, {Set<String> types = const {}}) {
final rects = <Map<String, int>>[];
void add(BuildContext c) {
final box = c.findRenderObject();
if (box is! RenderBox || !box.attached || !box.hasSize) return;
final full = box.localToGlobal(Offset.zero) & box.size;
var r = full;
// Clip to every enclosing scroll viewport so scrolled content isn't masked over app bars / nav.
// The paint clip is null for Clip.none viewports, which still paint (and must mask) overflow.
try {
RenderObject child = box;
for (var p = box.parent; p is RenderObject; child = p, p = p.parent) {
if (p is RenderBox && p is RenderAbstractViewport && p.hasSize) {
final clip = p.describeApproximatePaintClip(child);
if (clip != null) r = r.intersect(clip.shift(p.localToGlobal(Offset.zero)));
}
}
} catch (e) {
r = full; // never drop a mask because clipping failed; fall back to the full rect
plotlineDebugLog("Error clipping replay mask rect: $e");
}
if (r.width <= 0 || r.height <= 0) return;
rects.add({
"x": (r.left * pixRatio).round(),
"y": (r.top * pixRatio).round(),
"w": (r.width * pixRatio).round(),
"h": (r.height * pixRatio).round(),
});
}
void walk(Element e) {
if (_isAutoMasked(e.widget, types)) { add(e); return; }
e.visitChildren(walk);
}
// ponytail: full tree walk once per capture; cache per frame if a huge tree ever shows up in profiles
if (types.isNotEmpty) WidgetsBinding.instance.rootElement?.visitChildren(walk);
PlotlineSensitive.contexts.forEach(add);
return rects;
}