close method
Retargets the spring toward closed; from rest the close velocity hint scales with the flight's pixel travel so a far close lands heavier.
result is what closed completes with at finalization - "what
did the user pick". Every close overwrites it (a dismissal after
a value-carrying close honestly reports null).
Implementation
void close({double? velocity, Object? result}) {
if (_finished || (controller.target == 0 && !controller.isScrubbing)) {
return;
}
_result = result;
_removeHistoryEntry();
refreshSourceRect();
double? v = velocity;
// Value space normalizes distance, so the flight's pixel scale
// re-enters the physics here: a far close gets a bigger velocity
// injection - it lands heavier and bounces more visibly. Only from
// rest: a live interruption has its own velocity. A scrub whose
// value stands still (a committed predictive back) counts as rest -
// its zero velocity would starve the landing bump.
if (v == null &&
!controller.isAnimating &&
(!controller.isScrubbing || controller.velocity == 0)) {
final double travel =
(lastTargetRect.center - sourceRect.center).distance;
v =
controller.effectiveMotion.closeVelocityHint *
morphCloseHintScale(travel);
}
controller.close(velocity: v);
}