Popover
An anchored, dismissible overlay for supplementary interactive content.
RemixPopover displays rich content next to a trigger. It uses NakedPopover
for positioning, focus restoration, keyboard activation, outside-tap dismissal,
and programmatic control while Mix styles the overlay surface.
Use a popover for contextual details, lightweight forms, previews, and actions.
Use RemixTooltip for short, non-interactive hints and RemixDialog when the
user must address modal content before continuing.
Basic usage
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
import 'ui/ui.dart';
class AccountPopover extends StatelessWidget {
const AccountPopover({super.key});
@override
Widget build(BuildContext context) {
return FortalPopover(
semanticLabel: 'Show account details',
positioning: const OverlayPositionConfig(
side: OverlaySide.bottom,
alignment: OverlayAlignment.center,
),
popoverChild: const SizedBox(
width: 240,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Signed in as'),
SizedBox(height: 8),
Text('person@example.com'),
],
),
),
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10),
child: Text('Account'),
),
);
}
}The child is the trigger surface. RemixPopover supplies its tap, keyboard,
focus, and button semantics, so the trigger normally should be visual content
rather than another independently interactive button.
Programmatic control
Provide a Flutter MenuController and disable openOnTap when another event
owns the open state.
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
final controller = MenuController();
final controlledPopover = RemixPopover(
controller: controller,
openOnTap: false,
popoverChild: const Text('Controlled content'),
child: TextButton(
onPressed: () => controller.open(),
child: const Text('Open controlled popover'),
),
);
void closePopover() => controller.close();With openOnTap: false, the child owns activation and its accessibility
semantics. Give an interactive child its own accessible name and action, or
provide an equivalent accessible control elsewhere. The popover preserves
those semantics and adds its expanded/collapsed state.
onOpenRequested and onCloseRequested can delay or animate a transition.
The request callbacks must invoke their provided showOverlay or hideOverlay
callback to complete the state change.
Positioning
OverlayPositionConfig places the overlay on a trigger side, aligns it along
that side, and can apply an additional offset.
import 'package:remix/remix.dart';
const positioning = OverlayPositionConfig(
side: OverlaySide.top,
alignment: OverlayAlignment.end,
sideOffset: 8,
);The overlay is clamped to the available screen bounds.
Styling
PopoverStyler styles the overlay container. The trigger keeps its own
visual styling.
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
final styledPopover = RemixPopover(
style: PopoverStyler()
.padding(.all(16))
.constraints(BoxConstraintsMix(maxWidth: 320))
.color(Colors.white)
.borderRadius(.all(const Radius.circular(12))),
popoverChild: const Text('Custom popover'),
child: const Text('Open'),
);The FortalPopover preset defaults to FortalPopoverSize.size2, adds Fortal
spacing, border, radius, surface color, and shadow, constrains the surface to a
maximum width of 480 logical pixels, and renders no arrow. Content remains
fully composable.
Keyboard and accessibility
- Tap the trigger or press Space or Enter while it is focused to toggle the popover.
- Press Escape to close it and return focus to the trigger.
- Clicking outside closes the overlay.
consumeOutsideTapscontrols whether that tap reaches the widget behind it. - Use
semanticLabelwhen the built-in trigger's visual content does not provide a clear accessible name. WithopenOnTap: false, label the interactive child instead. - Put interactive content in a logical focus order. Wrap complex content in
FocusTraversalGroupwhen it needs a custom traversal policy.
Constructor
import 'package:flutter/material.dart';
import 'package:remix/remix.dart';
RemixPopover remixPopoverConstructor({
Key? key,
required Widget popoverChild,
required Widget child,
OverlayPositionConfig positioning = const OverlayPositionConfig(),
bool consumeOutsideTaps = true,
bool useRootOverlay = false,
bool openOnTap = true,
FocusNode? triggerFocusNode,
VoidCallback? onOpen,
VoidCallback? onClose,
RawMenuAnchorOpenRequestedCallback? onOpenRequested,
RawMenuAnchorCloseRequestedCallback? onCloseRequested,
MenuController? controller,
String? semanticLabel,
bool excludeSemantics = false,
PopoverStyler style = const PopoverStyler.create(),
PopoverSpec? styleSpec,
}) => throw UnimplementedError();Style methods
The styler includes the standard Remix container methods, including container,
padding, margin, alignment, color, border,
borderRadius, shadow, constraints, decoration, foregroundDecoration,
transform, animate, variants, wrap, and modifier.