MorphTargetSpec.popover constructor

MorphTargetSpec.popover({
  1. required Rect anchor,
  2. Size size = const Size(260, 300),
  3. double gap = 10,
  4. double margin = 12,
  5. MorphSurfaceSpec? surface,
  6. ShapeBorder? shape,
  7. Color? surfaceColor,
})

A popover anchored to the control that summoned it (the iOS 26 button-to-menu shape). Prefers sitting below the anchor, flips above when there is no room, and stays inside the overlay with margin. Capture the anchor rect at tap time with morphAnchorRect - it measures in the coordinates of the overlay the flight renders in, so a popover inside a nested navigator stays anchored instead of drifting by the navigator's own offset.

Implementation

factory MorphTargetSpec.popover({
  required Rect anchor,
  Size size = const Size(260, 300),
  double gap = 10,
  double margin = 12,
  MorphSurfaceSpec? surface,
  ShapeBorder? shape,
  Color? surfaceColor,
}) {
  return MorphTargetSpec(
    surface: surface,
    shape:
        shape ??
        const RoundedRectangleBorder(borderRadius: .all(.circular(20))),
    surfaceColor: surfaceColor,
    rectFor: (Size overlay, EdgeInsets padding) {
      // All four safe-area sides count: a home indicator or a
      // landscape notch is system chrome the popover must not sit
      // under.
      final double leftMin = padding.left + margin;
      final double leftMax = math.max(
        leftMin,
        overlay.width - padding.right - size.width - margin,
      );
      final double left = (anchor.center.dx - size.width / 2).clamp(
        leftMin,
        leftMax,
      );
      double top = anchor.bottom + gap;
      if (top + size.height > overlay.height - padding.bottom - margin) {
        top = anchor.top - gap - size.height;
      }
      final double topMin = padding.top + margin;
      final double topMax = math.max(
        topMin,
        overlay.height - padding.bottom - size.height - margin,
      );
      top = top.clamp(topMin, topMax);
      return Rect.fromLTWH(left, top, size.width, size.height);
    },
  );
}