showPopover<T> function

PopoverCompleter<T?> showPopover<T>({
  1. required BuildContext context,
  2. required AlignmentGeometry alignment,
  3. required WidgetBuilder builder,
  4. Offset? position,
  5. AlignmentGeometry? anchorAlignment,
  6. PopoverConstraint widthConstraint = PopoverConstraint.flexible,
  7. PopoverConstraint heightConstraint = PopoverConstraint.flexible,
  8. Key? key,
  9. bool rootOverlay = true,
  10. bool modal = true,
  11. Clip clipBehavior = Clip.none,
  12. Object? regionGroupId,
  13. Offset? offset,
  14. Alignment? transitionAlignment,
  15. EdgeInsets? margin,
  16. bool follow = true,
  17. bool consumeOutsideTaps = true,
  18. ValueChanged<PopoverAnchorState>? onTickFollow,
  19. bool allowInvertHorizontal = true,
  20. bool allowInvertVertical = true,
  21. bool dismissBackdropFocus = true,
  22. Duration? showDuration,
  23. Duration? dismissDuration,
})

Implementation

PopoverCompleter<T?> showPopover<T>({
  required BuildContext context,
  required AlignmentGeometry alignment,
  required WidgetBuilder builder,
  Offset? position,
  AlignmentGeometry? anchorAlignment,
  PopoverConstraint widthConstraint = PopoverConstraint.flexible,
  PopoverConstraint heightConstraint = PopoverConstraint.flexible,
  Key? key,
  bool rootOverlay = true,
  bool modal = true,
  Clip clipBehavior = Clip.none,
  Object? regionGroupId,
  Offset? offset,
  Alignment? transitionAlignment,
  EdgeInsets? margin,
  bool follow = true,
  bool consumeOutsideTaps = true,
  ValueChanged<PopoverAnchorState>? onTickFollow,
  bool allowInvertHorizontal = true,
  bool allowInvertVertical = true,
  bool dismissBackdropFocus = true,
  Duration? showDuration,
  Duration? dismissDuration,
}) {
  builder = Pylon.mirror(context, builder);
  TextDirection textDirection = Directionality.of(context);
  Alignment resolvedAlignment = alignment.resolve(textDirection);
  anchorAlignment ??= alignment * -1;
  Alignment resolvedAnchorAlignment = anchorAlignment.resolve(textDirection);
  final OverlayState overlay = Overlay.of(context, rootOverlay: rootOverlay);
  final themes = InheritedTheme.capture(from: context, to: overlay.context);
  final data = Data.capture(from: context, to: overlay.context);

  Size? anchorSize;
  if (position == null) {
    RenderBox renderBox = context.findRenderObject() as RenderBox;
    Offset pos = renderBox.localToGlobal(Offset.zero);
    anchorSize ??= renderBox.size;
    position = Offset(
      pos.dx +
          anchorSize.width / 2 +
          anchorSize.width / 2 * resolvedAnchorAlignment.x,
      pos.dy +
          anchorSize.height / 2 +
          anchorSize.height / 2 * resolvedAnchorAlignment.y,
    );
  }

  ValueNotifier<bool> isClosed = ValueNotifier(false);
  OverlayEntry? barrierEntry;
  late OverlayEntry overlayEntry;
  if (modal) {
    if (consumeOutsideTaps) {
      barrierEntry = OverlayEntry(
        builder: (context) {
          return GestureDetector(
            onTap: () {
              isClosed.value = true;
            },
          );
        },
      );
    } else {
      barrierEntry = OverlayEntry(
        builder: (context) {
          return Listener(
            behavior: HitTestBehavior.translucent,
            onPointerDown: (event) {
              isClosed.value = true;
            },
          );
        },
      );
    }
  }
  final _OverlayPopoverEntry<T> popoverEntry = _OverlayPopoverEntry();
  final completer = popoverEntry.completer;
  final animationCompleter = popoverEntry.animationCompleter;
  overlayEntry = OverlayEntry(
    builder: (innerContext) {
      return RepaintBoundary(
        child: FocusScope(
          autofocus: dismissBackdropFocus,
          child: AnimatedBuilder(
              animation: isClosed,
              builder: (innerContext, child) {
                return AnimatedValueBuilder.animation(
                    value: isClosed.value ? 0.0 : 1.0,
                    initialValue: 0.0,
                    curve: isClosed.value
                        ? const Interval(0, 2 / 3)
                        : Curves.linear,
                    duration: isClosed.value
                        ? (showDuration ?? kDefaultDuration)
                        : (dismissDuration ??
                            const Duration(milliseconds: 100)),
                    onEnd: (value) {
                      if (value == 0.0 && isClosed.value) {
                        popoverEntry.remove();
                        popoverEntry.dispose();
                        animationCompleter.complete();
                      }
                    },
                    builder: (innerContext, animation) {
                      var popoverAnchor = PopoverAnchor(
                        animation: animation,
                        onTapOutside: () {
                          if (isClosed.value) return;
                          if (!modal) {
                            isClosed.value = true;
                            completer.complete();
                          }
                        },
                        key: key,
                        anchorContext: context,
                        position: position!,
                        alignment: resolvedAlignment,
                        themes: themes,
                        builder: builder,
                        anchorSize: anchorSize,
                        // anchorAlignment: anchorAlignment ?? alignment * -1,
                        anchorAlignment: resolvedAnchorAlignment,
                        widthConstraint: widthConstraint,
                        heightConstraint: heightConstraint,
                        regionGroupId: regionGroupId,
                        offset: offset,
                        transitionAlignment: transitionAlignment,
                        margin: margin,
                        follow: follow,
                        consumeOutsideTaps: consumeOutsideTaps,
                        onTickFollow: onTickFollow,
                        allowInvertHorizontal: allowInvertHorizontal,
                        allowInvertVertical: allowInvertVertical,
                        data: data,
                        onClose: () {
                          if (isClosed.value) return Future.value();
                          isClosed.value = true;
                          completer.complete();
                          return animationCompleter.future;
                        },
                        onImmediateClose: () {
                          popoverEntry.remove();
                          completer.complete();
                        },
                        onCloseWithResult: (value) {
                          if (isClosed.value) return Future.value();
                          isClosed.value = true;
                          completer.complete(value as T);
                          return animationCompleter.future;
                        },
                      );
                      return popoverAnchor;
                    });
              }),
        ),
      );
    },
  );
  popoverEntry.overlayEntry = overlayEntry;
  popoverEntry.barrierEntry = barrierEntry;
  if (barrierEntry != null) {
    overlay.insert(barrierEntry);
  }
  overlay.insert(overlayEntry);
  return popoverEntry;
}