computeMorphFrame function
- required double value,
- required Rect sourceRect,
- required Rect targetRect,
- required ShapeBorder sourceShape,
- required ShapeBorder targetShape,
- required Color sourceColor,
- required Color targetColor,
- required double maxScrimOpacity,
- double sourceElevation = 0,
- double targetElevation = 24,
Computes every render value of a flight frame from one spring
value - the whole visual contract in a single pure function.
Implementation
MorphFrame computeMorphFrame({
required double value,
required Rect sourceRect,
required Rect targetRect,
required ShapeBorder sourceShape,
required ShapeBorder targetShape,
required Color sourceColor,
required Color targetColor,
required double maxScrimOpacity,
double sourceElevation = 0,
double targetElevation = 24,
}) {
final double p = clampDouble(value, 0, 1);
final ({Rect rect, double? sourceRadius, double? targetRadius}) geometry =
morphFlightGeometry(
value: value,
sourceRect: sourceRect,
targetRect: targetRect,
sourceShape: sourceShape,
targetShape: targetShape,
);
final Rect rect = geometry.rect;
final double targetReveal = _targetFade.transform(p);
double? cornerRadius;
ShapeBorder shape;
if (geometry.sourceRadius == null || geometry.targetRadius == null) {
shape = ShapeBorder.lerp(sourceShape, targetShape, p) ?? targetShape;
} else {
cornerRadius = morphConcentricRadius(
geometry.sourceRadius!,
geometry.targetRadius!,
p,
rect,
);
shape = _shapeFromRadius(sourceShape, targetShape, cornerRadius);
}
return MorphFrame(
rect: rect,
shape: shape,
cornerRadius: cornerRadius,
sourceOpacity: 1 - _sourceFade.transform(p),
targetOpacity: targetReveal,
targetScale: lerpDouble(0.95, 1, targetReveal)!,
scrimOpacity: maxScrimOpacity * _scrimFade.transform(p),
// The shadow belongs to the morphing container (the Material
// container-transform guideline): the shuttle starts with exactly
// the button's shadow and grows it continuously into the container
// one - no shadow pop when the tag hides or at handoff. The lerp
// rides p^1.5, not p: linear shadow reads as a hover near the
// source end (a button-sized container carrying a quarter of the
// dialog's elevation floats a layer above its resting self, and a
// slow profile makes that a scene), while too-flat a curve starves
// the middle of the "card" cue - a big surface with a button's
// shadow reads as the button deformed, not a dialog materializing.
// p^1.5 keeps the settle tail at the button's own shadow and gives
// the mid-flight surface its card depth; symmetric, still a pure
// function of the value.
elevation: lerpDouble(sourceElevation, targetElevation, p * math.sqrt(p))!,
surfaceColor: Color.lerp(
sourceColor,
targetColor,
_surfaceBlend.transform(p),
)!,
);
}