What are popup hosts?
Popup hosts are widgets that automatically show and dismiss dialogs, fullscreen overlays, or modal presentations based on Presentum surface state. They build on top of surface observers to add popup-specific features:- Duplicate detection - Prevent showing the same popup twice in quick succession
- Conflict resolution - Configure what happens when popups overlap
- Automatic queuing - Queue popups to show sequentially
- Smart dismissal tracking - Only track dismissals for system-closed popups
Prerequisites
ThePresentumPopupSurfaceStateMixin requires PresentumActiveSurfaceItemObserverMixin as a base. If you havenβt used surface observers before, read the Surface observers guide first.
Basic usage
Hereβs a minimal popup host:Required implementations
You must implement two members:PopupPresentResult values
Return the appropriate enum value based on how the popup was dismissed:-
PopupPresentResult.userDismissed- User took action (e.g., clicked βBuy Nowβ, converted). The mixin will NOT callmarkDismissed()because you should have already handled it. -
PopupPresentResult.systemDismissed- User closed the popup without taking action (e.g., clicked βXβ or βDismissβ). The mixin WILL callmarkDismissed()automatically. -
PopupPresentResult.notPresented- Could not show the popup (e.g., widget not mounted). The mixin will NOT callmarkDismissed().
The mixin provides a default
markDismissed() implementation that delegates
to context.presentum().markDismissed(). Override only if you need custom
dismissal logic.What the mixin provides
ThePresentumPopupSurfaceStateMixin automatically:
- Observes surface state via the required observer mixin
- Shows popups when active item changes
- Dismisses popups when active item becomes null
- Prevents duplicates with optional duplicate detection
- Resolves conflicts when multiple popups activate
- Manages queue for sequential popup display
- Tracks dismissals intelligently based on
PopupPresentResult
Optional configuration
Duplicate detection
Prevent showing the same popup multiple times in quick succession:ignoreDuplicates: Enable/disable duplicate detection (default:false)duplicateThreshold: Time window for considering entries duplicates- If
null, always ignore duplicates of the same ID - If set, only ignore if shown within the threshold
- If
Conflict strategies
Control what happens when a new popup activates while another is showing:Ignore (default)
Keep showing the current popup, ignore new ones:Replace
Immediately dismiss the current popup and show the new one:Queue
Queue new popups to show after the current one is dismissed:Queuing behavior example
When usingPopupConflictStrategy.queue:
ignore: Items B and C are ignored while A showsreplace: A is dismissed, B shows immediately (then C replaces B)
Production example
This is the actual popup host from the example app, managing campaign dialogs and fullscreen promos:Notice
markDismissed is not implemented. The default implementation handles
it automatically.Custom presentation
Fullscreen routes
UseNavigator.push instead of showDialog for fullscreen presentations:
Bottom sheets
UseshowModalBottomSheet for bottom sheet presentations:
Custom pop behavior
Overridepop() for custom navigation:
Dialog widgets
Create dialog widgets that access the item viaInheritedPresentumItem:
Integration
Initialize the presentum and wrap your app with the popup host:This pattern initializes the presentum engine, wraps the child with the
provider scope for context access, and adds the popup host for automatic
dialog management.
Best practices
Always wrap with InheritedPresentumItem
Always wrap with InheritedPresentumItem
This ensures descendants can access the item and presentum instance:
Record impressions before showing
Record impressions before showing
Call
markShown before displaying the popup:Check mounted before presenting
Check mounted before presenting
Always check if the widget is mounted before showing popups:
Return correct PopupPresentResult
Return correct PopupPresentResult
Map dialog results to the appropriate enum value:This ensures dismissal tracking is only recorded for non-conversions.
Add both mixins in correct order
Add both mixins in correct order
Always add
PresentumActiveSurfaceItemObserverMixin before PresentumPopupSurfaceStateMixin:Advanced: Custom dismissal tracking
OverridemarkDismissed for custom logic:
When to use popup hosts
UsePresentumPopupSurfaceStateMixin when you need:
Dialogs
Modal dialogs that require user interaction
Fullscreen pages
Fullscreen promotional content or onboarding flows
Bottom sheets
Modal bottom sheets with actions
Conflict resolution
Smart handling of overlapping popups with queueing
Next steps
Surface observers
Learn about the underlying observer mixin
Inherited widgets
Using InheritedPresentumItem in dialogs
Production example
Complete popup host implementation
Example app
Explore the full example application