showDropdown<T> function

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

Shows a dropdown menu overlay at a specified position or anchored to a widget.

Creates and displays a dropdown menu as an overlay, with full control over positioning, alignment, sizing, and behavior. Returns an OverlayCompleter that resolves when the menu is dismissed, optionally with a selected value.

This is a low-level function typically used by higher-level dropdown components. For most use cases, prefer using DropdownMenu or similar widgets.

Key Features:

  • Flexible Positioning: Anchor to widget or absolute position
  • Auto-sizing: Constraint-based width/height
  • Smart Alignment: Auto-inverts to stay on screen
  • Following: Menu follows anchor when it moves
  • Modal/Non-modal: Configurable interaction blocking

Parameters:

  • context: Build context for overlay insertion (required)
  • builder: Widget builder for menu content (required)
  • position: Absolute position override
  • anchorAlignment: Alignment on the anchor widget
  • widthConstraint: Width sizing behavior (defaults to flexible)
  • heightConstraint: Height sizing behavior (defaults to flexible)
  • key: Key for the overlay widget
  • rootOverlay: Use root overlay vs nearest (defaults to true)
  • modal: Block interactions outside menu (defaults to true)
  • clipBehavior: How to clip overflow (defaults to Clip.none)
  • regionGroupId: Grouping ID for related overlays
  • offset: Additional offset from anchor
  • transitionAlignment: Alignment for show/hide transitions
  • alignment: Menu alignment relative to anchor
  • margin: Outer margin around menu
  • follow: Track anchor movement (defaults to true)
  • consumeOutsideTaps: Dismiss on outside taps (defaults to false)
  • onTickFollow: Callback during follow updates
  • allowInvertHorizontal: Allow horizontal flip to stay on screen (defaults to true)
  • allowInvertVertical: Allow vertical flip to stay on screen (defaults to true)
  • dismissBackdropFocus: Dismiss when clicking backdrop (defaults to true)
  • showDuration: Custom show animation duration
  • dismissDuration: Custom dismiss animation duration

Returns an OverlayCompleter that resolves to the selected value when dismissed.

Example:

final result = showDropdown<String>(
  context: context,
  builder: (context) => DropdownMenu(
    children: [
      MenuItem(title: Text('Option 1'), onTap: () => Navigator.pop(context, 'opt1')),
      MenuItem(title: Text('Option 2'), onTap: () => Navigator.pop(context, 'opt2')),
    ],
  ),
);
final selected = await result.future;
print('Selected: $selected');

Implementation

OverlayCompleter<T?> showDropdown<T>({
  required BuildContext context,
  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,
  AlignmentGeometry? transitionAlignment,
  AlignmentGeometry? alignment,
  EdgeInsetsGeometry? margin,
  bool follow = true,
  bool consumeOutsideTaps = false,
  ValueChanged<PopoverOverlayWidgetState>? onTickFollow,
  bool allowInvertHorizontal = true,
  bool allowInvertVertical = true,
  bool dismissBackdropFocus = true,
  Duration? showDuration,
  Duration? dismissDuration,
}) {
  final theme = Theme.of(context);
  final scaling = theme.scaling;
  final GlobalKey key = GlobalKey();
  final overlayManager = OverlayManager.of(context);
  return overlayManager.showMenu<T>(
    context: context,
    alignment: alignment ?? Alignment.topCenter,
    offset: offset ?? (const Offset(0, 4) * scaling),
    follow: follow,
    clipBehavior: clipBehavior,
    margin: margin,
    transitionAlignment: transitionAlignment,
    onTickFollow: onTickFollow,
    allowInvertHorizontal: allowInvertHorizontal,
    allowInvertVertical: allowInvertVertical,
    showDuration: showDuration,
    dismissDuration: dismissDuration,
    widthConstraint: widthConstraint,
    heightConstraint: heightConstraint,
    position: position,
    anchorAlignment: anchorAlignment,
    consumeOutsideTaps: consumeOutsideTaps,
    regionGroupId: key,
    modal: modal,
    dismissBackdropFocus: dismissBackdropFocus,
    overlayBarrier: OverlayBarrier(
      borderRadius: BorderRadius.circular(theme.radiusMd),
    ),
    builder: (context) {
      return Data.inherit(
        data: DropdownMenuData(key),
        child: builder(context),
      );
    },
  );
}