I am trying to redact information from a screenshot via drawing a black rectangle over the entirety of a given RenderBox object. However, during testing I noticed that a simple TextField object with an OutlineInputBorder
will cause the labelText to "bleed" into the margins above the text box when focused.
Ideally I'm ultimately looking for a way to determine with certainty the exact bounds I'd need to redact on any RenderBox object, not just this specific type of text box. This is just a common / simple example I discovered.
not focused:
focused:
When I try to get the coordinates and dimensions to draw the redaction rectangle over the entire element the label text inevitably bleeds outside the rectangle and becomes visible when focused:
I'm looking for a way to programmatically determine the actual bounds an element is rendered in so that I can be confident I am obscuring all of it. From what I can tell the amount an element bleeds over into padding / other elements is arbitrary and it's critical that edge cases don't result in unobscured info.
The only other avenue I can think of is to search the children of the text box object and find the label as a text element and try to redact that (assuming it's a separate element) but I didn't see any obvious child elements like that and I'd like to avoid exhaustively searching all child elements if possible.
Example text field:
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
child: Redact( // custom class that signals should be redacted
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Redacted Input 1',
),
autocorrect: false,
enableSuggestions: false,
),
),
),
Code to determine dimensions. I tried using size
and paintBounds
but results were the same:
if (renderObject is RenderBox && renderObject.hasSize) {
var position = renderObject.localToGlobal(Offset.zero);
var objectWidth = renderObject.paintBounds.width;
var objectHeight = renderObject.paintBounds.height;
redactionRects.add(Rect.fromLTWH(
position.dx,
position.dy,
objectWidth,
objectHeight,
));
}
I also tried looking at parentData
to try and deal with edge cases where the parent was a padding object specifically but that wasn't sufficient and reinforced my understanding that the amount elements can bleed over is somewhat arbitrary and requires a more thorough solution.
if (renderObject.parentData is BoxParentData) {
final parentData = renderObject.parentData as BoxParentData;
position -= parentData.offset;
// assume offset is symmetric
objectWidth += (parentData.offset.dx * 2);
objectHeight += (parentData.offset.dy * 2);
}
Very slightly improved results:
Any help or suggestions greatly appreciated. I am somewhat new to Flutter!
So it turns out my initial intuition about going through the children was correct. Something similar to this where you iterate on the child elements, while perhaps not performant in some cases, offers much better guarantees about redacting all related info.
// redact any descendant Rects that are not fully contained within the parent
Queue<Element> redactedChildrenQueue = Queue.from([currentElement]);
while (redactedChildrenQueue.isNotEmpty) {
final redactedChild = redactedChildrenQueue.removeFirst();
final childRect = _getGlobalElementRect(redactedChild);
if (!childRect.isEmpty && childRect.intersect(elementRect) != childRect) {
redactionRects.add(childRect);
}
redactedChild.visitChildElements((child) {
redactedChildrenQueue.add(child);
});
}
Result where label is redacted: