resolveAdaptiveShadMenuAnchor function

ShadAnchor resolveAdaptiveShadMenuAnchor(
  1. BuildContext context, {
  2. BuildContext? boundaryContext,
  3. double viewportVerticalSplit = 2 / 3,
  4. double gap = 8,
  5. double? estimatedMenuWidth,
  6. double? estimatedMenuHeight,
  7. double viewportEdgePadding = 12,
  8. bool centerHorizontallyInBoundary = false,
  9. ShadMenuHorizontalPosition horizontalPosition = ShadMenuHorizontalPosition.automatic,
  10. ShadMenuVerticalPosition verticalPosition = ShadMenuVerticalPosition.automatic,
})

Implementation

ShadAnchor resolveAdaptiveShadMenuAnchor(
  BuildContext context, {
  BuildContext? boundaryContext,
  double viewportVerticalSplit = 2 / 3,
  double gap = 8,
  double? estimatedMenuWidth,
  double? estimatedMenuHeight,
  double viewportEdgePadding = 12,
  bool centerHorizontallyInBoundary = false,
  ShadMenuHorizontalPosition horizontalPosition = ShadMenuHorizontalPosition.automatic,
  ShadMenuVerticalPosition verticalPosition = ShadMenuVerticalPosition.automatic,
}) {
  const fallbackAnchor = ShadAnchor(childAlignment: Alignment.topLeft, overlayAlignment: Alignment.bottomLeft, offset: Offset(0, 8));
  final renderBox = _safeRenderBox(context);
  if (renderBox == null) {
    return fallbackAnchor;
  }

  final overlayRenderBox = _safeRenderBox(Overlay.maybeOf(context)?.context);
  final boundaryRenderBox = _safeRenderBox(boundaryContext) ?? overlayRenderBox;

  try {
    final viewportSize = boundaryRenderBox?.size ?? MediaQuery.sizeOf(context);
    final triggerOrigin = boundaryRenderBox != null
        ? renderBox.localToGlobal(Offset.zero, ancestor: boundaryRenderBox)
        : renderBox.localToGlobal(Offset.zero);
    final triggerBottom = triggerOrigin.dy + renderBox.size.height;
    final triggerRight = triggerOrigin.dx + renderBox.size.width;
    final triggerCenter = triggerOrigin + Offset(renderBox.size.width / 2, renderBox.size.height / 2);
    final viewportCenterX = viewportSize.width / 2;

    final openDown = switch (verticalPosition) {
      ShadMenuVerticalPosition.down => true,
      ShadMenuVerticalPosition.up => false,
      ShadMenuVerticalPosition.automatic => _shouldOpenMenuDown(
        triggerOriginY: triggerOrigin.dy,
        triggerBottomY: triggerBottom,
        triggerCenterY: triggerCenter.dy,
        viewportHeight: viewportSize.height,
        viewportVerticalSplit: viewportVerticalSplit,
        estimatedMenuHeight: estimatedMenuHeight,
        viewportEdgePadding: viewportEdgePadding,
      ),
    };

    if (centerHorizontallyInBoundary) {
      return ShadAnchor(
        childAlignment: Alignment(0, openDown ? -1 : 1),
        overlayAlignment: Alignment(0, openDown ? 1 : -1),
        offset: Offset(viewportCenterX - triggerCenter.dx, openDown ? gap : -gap),
      );
    }

    final alignLeft = _shouldAlignMenuLeft(
      triggerOriginX: triggerOrigin.dx,
      triggerRightX: triggerRight,
      triggerCenterX: triggerCenter.dx,
      viewportWidth: viewportSize.width,
      viewportEdgePadding: viewportEdgePadding,
      estimatedMenuWidth: estimatedMenuWidth,
      horizontalPosition: horizontalPosition,
    );

    return ShadAnchor(
      childAlignment: Alignment(alignLeft ? -1 : 1, openDown ? -1 : 1),
      overlayAlignment: Alignment(alignLeft ? -1 : 1, openDown ? 1 : -1),
      offset: Offset(0, openDown ? gap : -gap),
    );
  } catch (_) {
    return fallbackAnchor;
  }
}