release method
Implementation
Future<void> release(Release plan) async {
final scaleHasMotion =
plan.scale.decay.isNotEmpty || plan.scale.settle != null;
if (!scaleHasMotion) {
// No scale animation → axes are independent.
setRect(rect.value);
await Future.wait([
_runAxis(plan.x, animateCenterX),
_runAxis(plan.y, animateCenterY),
_runAxis(plan.scale, animateWidth),
]);
return;
}
// Decomposed-release: scale drives the proportional center; X/Y add a
// translation offset on top. Each axis keeps its own curve/duration and
// the displayed center stays consistent mid-frame.
final initialRect = rect.value;
final wFinal = plan.scale.settle?.to ?? plan.scale.decay.last.to;
final scaleRatio = initialRect.width == 0 ? 1.0 : wFinal / initialRect.width;
final xPropFinal = display.rect.center.dx
+ (initialRect.center.dx - display.rect.center.dx) * scaleRatio;
final yPropFinal = display.rect.center.dy
+ (initialRect.center.dy - display.rect.center.dy) * scaleRatio;
final xAbsTarget = plan.x.settle?.to
?? (plan.x.decay.isNotEmpty ? plan.x.decay.last.to : initialRect.center.dx);
final yAbsTarget = plan.y.settle?.to
?? (plan.y.decay.isNotEmpty ? plan.y.decay.last.to : initialRect.center.dy);
final xOffsetTarget = xAbsTarget - xPropFinal;
final yOffsetTarget = yAbsTarget - yPropFinal;
setReleaseDecomposed(
initialX: initialRect.center.dx,
initialY: initialRect.center.dy,
initialWidth: initialRect.width,
displayCenter: display.rect.center,
);
try {
await Future.wait([
animateCenterX(
to: xOffsetTarget,
duration: plan.x.settle?.duration,
curve: plan.x.settle?.curve ?? Curves.easeOut,
),
animateCenterY(
to: yOffsetTarget,
duration: plan.y.settle?.duration,
curve: plan.y.settle?.curve ?? Curves.easeOut,
),
_runAxis(plan.scale, animateWidth),
]);
} finally {
clearReleaseDecomposed();
}
}