computeMorphFrame function

MorphFrame computeMorphFrame({
  1. required double value,
  2. required Rect sourceRect,
  3. required Rect targetRect,
  4. required ShapeBorder sourceShape,
  5. required ShapeBorder targetShape,
  6. required Color sourceColor,
  7. required Color targetColor,
  8. required double maxScrimOpacity,
  9. double sourceElevation = 0,
  10. 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),
    )!,
  );
}