showContextMenu function

OverlayEntry showContextMenu({
  1. required BuildContext context,
  2. required PreferredSizeWidget child,
  3. required Color backgroundColor,
  4. bool opaque = false,
  5. double spacing = 16,
  6. double arrowSize = 12,
  7. Duration duration = const Duration(milliseconds: 200),
})

Implementation

OverlayEntry showContextMenu({
  required BuildContext context,
  required PreferredSizeWidget child,
  required Color backgroundColor,
  bool opaque = false,
  double spacing = 16,
  double arrowSize = 12,
  Duration duration = const Duration(milliseconds: 200),
}) {
  final renderBox = context.findRenderObject() as RenderBox?;

  if (renderBox == null) {
    throw Exception('RenderBox not found');
  }

  final size = renderBox.size;

  final offset = renderBox.localToGlobal(Offset.zero);

  final screenWidth = MediaQuery.of(context).size.width;

  final overflowsTop = offset.dy - child.preferredSize.height < 0;

  final topInset = overflowsTop
      ? offset.dy + size.height
      : offset.dy - child.preferredSize.height - spacing;

  final overflowsRight = offset.dx + child.preferredSize.width > screenWidth;

  final leftInset = overflowsRight
      ? screenWidth - child.preferredSize.width - spacing
      : offset.dx;

  final arrowLeft = offset.dx + size.width / 2;

  late final OverlayEntry _entry;

  _entry = OverlayEntry(
    opaque: opaque,
    builder: (context) => GestureDetector(
      onTap: () {
        _entry.remove();
      },
      child: ColoredBox(
        color: Colors.transparent,
        child: Stack(
          children: [
            Positioned(
              top: topInset,
              left: leftInset,
              child: FadeIn(
                duration: duration,
                child: child,
              ),
            ),
            Positioned(
              top: offset.dy - arrowSize - (spacing - arrowSize),
              left: arrowLeft - arrowSize / 2,
              child: FadeIn(
                duration: duration,
                child: CustomPaint(
                  painter: TrianglePainter(
                    strokeColor: backgroundColor,
                  ),
                  size: Size(arrowSize, arrowSize),
                ),
              ),
            ),
          ],
        ),
      ),
    ),
  );

  Overlay.of(context, rootOverlay: true).insert(_entry);

  return _entry;
}