WPopover
A flexible popover component for creating dropdown menus, notification panels, user menus, tooltips, and similar overlay patterns.
On this page
popover/popover_basic
The live demo is not available for this example.
popover_basic.dart
View source
WPopover(
alignment: PopoverAlignment.bottomRight,
className: 'w-64 bg-white dark:bg-gray-800 rounded-xl shadow-xl p-2',
triggerBuilder: (context, isOpen, isHovering) => WButton(
className: 'bg-blue-600 text-white',
child: Text('Open Menu'),
),
contentBuilder: (context, close) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(title: Text('Profile'), onTap: close),
ListTile(title: Text('Settings'), onTap: close),
],
),
)
Basic Usage#
The WPopover widget uses two builders: triggerBuilder for the element that activates the popover, and contentBuilder for the overlay content. It manages the overlay state and positioning automatically.
WPopover(
triggerBuilder: (context, isOpen, isHovering) => WText(
'Click me',
className: 'text-blue-500 cursor-pointer',
),
contentBuilder: (context, close) => WDiv(
className: 'p-4',
child: WText('Hello from Popover!'),
),
)
Constructor#
const WPopover({
Key? key,
required PopoverTriggerBuilder triggerBuilder,
required PopoverContentBuilder contentBuilder,
PopoverController? controller,
bool enableTriggerOnTap = true,
PopoverAlignment alignment = PopoverAlignment.bottomLeft,
String? className,
Offset offset = const Offset(0, 4),
double maxHeight = 400,
bool disabled = false,
bool closeOnContentTap = false,
VoidCallback? onOpen,
VoidCallback? onClose,
bool autoFlip = true,
})
Props#
| Prop | Type | Default | Description |
|---|---|---|---|
triggerBuilder |
PopoverTriggerBuilder |
Required | Builder for the trigger widget. Receives isOpen and isHovering states. |
contentBuilder |
PopoverContentBuilder |
Required | Builder for the popover content. Receives a close callback. |
className |
String? |
null |
Wind utility classes for the popover container styling. |
controller |
PopoverController? |
null |
Optional controller for programmatic show/hide/toggle. |
alignment |
PopoverAlignment |
bottomLeft |
Where to position the popover relative to the trigger. |
offset |
Offset |
Offset(0, 4) |
Gap between the trigger and the popover. |
maxHeight |
double |
400 |
Maximum height for the content. It will scroll if exceeded. |
enableTriggerOnTap |
bool |
true |
Whether tapping the trigger toggles the popover. |
closeOnContentTap |
bool |
false |
Whether tapping inside the content closes the popover. |
disabled |
bool |
false |
When true, the trigger will not respond to interactions. |
onOpen |
VoidCallback? |
null |
Callback fired when the popover is opened. |
onClose |
VoidCallback? |
null |
Callback fired when the popover is closed. |
autoFlip |
bool |
true |
When true, the popover flips its alignment to the opposite side if the natural position would render off-screen. |
Layout Modes#
While WPopover itself isn't a layout container, it supports various Alignment Modes to determine how the overlay is positioned relative to the trigger.
Alignment#
WPopover intelligently "flips" the alignment if the requested position would cause the overlay to overflow the screen edges.
widgets/w_popover_alignment
The live demo is not available for this example.
w_popover_alignment.dart
View source
| Alignment | Description |
|---|---|
bottomLeft |
Below trigger, aligned to left edge (Default). |
bottomRight |
Below trigger, aligned to right edge. |
bottomCenter |
Below trigger, centered horizontally. |
topLeft |
Above trigger, aligned to left edge. |
topRight |
Above trigger, aligned to right edge. |
topCenter |
Above trigger, centered horizontally. |
Event Handling#
The triggerBuilder provides the isOpen and isHovering states, allowing you to reactively style the trigger. The contentBuilder provides a close callback to dismiss the popover from within the content (e.g., when a menu item is clicked).
WPopover(
triggerBuilder: (context, isOpen, isHovering) => WDiv(
className: 'p-2 rounded ${isOpen ? "bg-blue-100" : "bg-gray-100"}',
child: WText(isOpen ? 'Active' : 'Idle'),
),
contentBuilder: (context, close) => WButton(
onTap: close,
child: Text('Click to Close'),
),
)
State Variants#
Since the popover trigger is built via triggerBuilder, you can apply state-based styling manually or use Wind's state prefixes if the trigger is a Wind widget like WButton or WDiv.
WPopover(
triggerBuilder: (context, isOpen, isHovering) => WButton(
// Use the isOpen boolean for manual state toggling
className: 'p-2 rounded-lg ${isOpen ? "bg-blue-600 text-white" : "bg-white text-gray-800"}',
child: Text('Toggle Menu'),
),
contentBuilder: (context, close) => MyContent(),
)
Styling Examples#
Custom Size and Shadow#
If no width is specified in className, the popover uses the trigger's width as a minimum. Use sizing utilities to define a specific width.
WPopover(
className: 'w-80 bg-white rounded-2xl shadow-2xl border border-gray-100',
triggerBuilder: (context, isOpen, isHovering) => Icon(Icons.more_horiz),
contentBuilder: (context, close) => MyLargeMenu(),
)
Dark Mode#
WPopover fully supports dark mode styling using the dark: prefix.
WPopover(
className: 'bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-800 shadow-xl',
triggerBuilder: (context, isOpen, isHovering) => MyTrigger(),
contentBuilder: (context, close) => MyContent(),
)
All Supported Classes#
The popover overlay container is a WDiv and supports all standard Wind utilities.
| Category | Classes |
|---|---|
| Sizing | w-{size}, max-w-{size}, h-{size}, max-h-{size} |
| Background | bg-{color}, bg-opacity-{n} |
| Borders | border, border-{color}, rounded-{size} |
| Shadow | shadow-{size}, shadow-{color}/{opacity} |
| Padding | p-{n}, px-{n}, py-{n} |
Customizing Theme#
Popovers inherit from global theme scales. You can customize the default appearance by modifying the WindThemeData.
WindThemeData(
colors: {
'popover-bg': Colors.white,
},
baseSpacingUnit: 4.0,
)