hitTest method
Determines the set of render objects located at the given position.
Returns true, and adds any render objects that contain the point to the given hit test result, if this render object or one of its descendants absorbs the hit (preventing objects below this one from being hit). Returns false if the hit can continue to other objects below this one.
The caller is responsible for transforming position
from global
coordinates to its location relative to the origin of this RenderBox.
This RenderBox is responsible for checking whether the given position is
within its bounds.
If transforming is necessary, BoxHitTestResult.addWithPaintTransform,
BoxHitTestResult.addWithPaintOffset, or
BoxHitTestResult.addWithRawTransform need to be invoked by the caller
to record the required transform operations in the HitTestResult. These
methods will also help with applying the transform to position
.
Hit testing requires layout to be up-to-date but does not require painting to be up-to-date. That means a render object can rely upon performLayout having been called in hitTest but cannot rely upon paint having been called. For example, a render object might be a child of a RenderOpacity object, which calls hitTest on its children when its opacity is zero even though it does not paint its children.
Implementation
@override
bool hitTest(BoxHitTestResult result, {required Offset position}) {
if (!hasSize ||
!contentVisibilityHitTest(result, position: position) ||
renderStyle.isVisibilityHidden) {
return false;
}
assert(() {
if (!hasSize) {
if (debugNeedsLayout) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary(
'Cannot hit test a render box that has never been laid out.'),
describeForError(
'The hitTest() method was called on this RenderBox'),
ErrorDescription(
"Unfortunately, this object's geometry is not known at this time, "
'probably because it has never been laid out. '
'This means it cannot be accurately hit-tested.'),
ErrorHint('If you are trying '
'to perform a hit test during the layout phase itself, make sure '
"you only hit test nodes that have completed layout (e.g. the node's "
'children, after their layout() method has been called).'),
]);
}
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('Cannot hit test a render box with no size.'),
describeForError('The hitTest() method was called on this RenderBox'),
ErrorDescription(
'Although this node is not marked as needing layout, '
'its size is not set.'),
ErrorHint('A RenderBox object must have an '
'explicit size before it can be hit-tested. Make sure '
'that the RenderBox in question sets its size during layout.'),
]);
}
return true;
}());
bool isHit = result.addWithPaintTransform(
transform: renderStyle.transformMatrix != null ? getEffectiveTransform() : null,
position: position,
hitTest: (BoxHitTestResult result, Offset trasformPosition) {
return result.addWithPaintOffset(
offset: (scrollLeft != 0.0 || scrollTop != 0.0)
? Offset(-scrollLeft, -scrollTop)
: null,
position: trasformPosition,
hitTest: (BoxHitTestResult result, Offset position) {
CSSPositionType positionType = renderStyle.position;
if (positionType == CSSPositionType.fixed) {
position -= getTotalScrollOffset();
}
// Determine whether the hittest position is within the visible area of the node in scroll.
if ((clipX || clipY) && !size.contains(trasformPosition)) {
return false;
}
// addWithPaintOffset is to add an offset to the child node, the calculation itself does not need to bring an offset.
if (hitTestChildren(result, position: position) ||
hitTestSelf(trasformPosition)) {
result.add(BoxHitTestEntry(this, position));
return true;
}
return false;
});
},
);
return isHit;
}