focusOnChild method
void
focusOnChild(
- CanvasChildId id, {
- ScalingMode scalingMode = ScalingMode.keepScale,
- bool animate = true,
- double preferredHorizontalMargin = 16,
- Duration? duration,
- Size? childSize,
- dynamic forceRedraw = false,
Focus the viewport on a child by its ID, with a margin in screen-space. If it's already rendered, size will be picked up from the child widget. If not an offstage rendering will be used ( double render ) Preferred horizontal margin used for ScalingMode.fitInViewport. Fits rotated bounds in both viewport dimensions and centers using the resulting scale. Zoom limits take precedence over fitting; the child remains centered.
Implementation
void focusOnChild(
CanvasChildId id, {
ScalingMode scalingMode = ScalingMode.keepScale,
bool animate = true,
double preferredHorizontalMargin = 16,
Duration? duration,
Size? childSize,
forceRedraw = false,
}) {
final childInfo = _requireChild(id);
// try to figure out the size, take from render cache if available
// else do an offstage render
childSize ??= childInfo.lastRenderedSize != null && !forceRedraw
? childInfo.lastRenderedSize
: measureWidgetSize(_context, childInfo.widget);
_validateGeometry(childSize: childSize);
_updateChild(childInfo, id, childSize: childSize);
_markDirty = true;
final bounds = MatrixUtils.transformRect(
childTransform(childInfo.gsPosition, childSize!, childInfo.rotation),
Offset.zero & childSize,
);
/*
margin is symmetric on ltrb so
2mx + cx = screenWidth
2my + cy = screenHeight
where c is rotated bounds size in screen space at newScale and m is margin
centering in grid space is childCenter - screenCenter / newScale
*/
double newScale = _scale;
switch (scalingMode) {
case ScalingMode.keepScale:
// do nothing
break;
case ScalingMode.resetScale:
newScale = 1;
case ScalingMode.fitInViewport:
// the scale needs to be determined in this case
// the horizontal margin constrains x, viewport height constrains y; use the smaller scale
final horizontalScale = bounds.width == 0
? double.infinity
: (canvasSize.width - 2 * preferredHorizontalMargin) / bounds.width;
final verticalScale = bounds.height == 0
? double.infinity
: canvasSize.height / bounds.height;
final fitScale = min(horizontalScale, verticalScale);
if (fitScale.isFinite && fitScale > 0) newScale = fitScale;
break;
}
newScale = _clampScale(newScale);
final newGsTopLeft = bounds.center - _ssCenter / newScale;
if (animate) {
animateToOffsetAndScale(
offset: newGsTopLeft,
duration: duration,
scale: newScale,
);
} else {
stopAnimation();
_gsTopLeftOffset = newGsTopLeft;
_scale = newScale;
markDirty();
}
}