Skip to main content

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
Use popup hosts for dialogs, fullscreen pages, bottom sheets, and any modal UI that requires user interaction and navigation integration.

Prerequisites

The PresentumPopupSurfaceStateMixin requires PresentumActiveSurfaceItemObserverMixin as a base. If you haven’t used surface observers before, read the Surface observers guide first.
Both mixins are required. You must add PresentumActiveSurfaceItemObserverMixin before PresentumPopupSurfaceStateMixin in your mixin list.

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 call markDismissed() 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 call markDismissed() automatically.
  • PopupPresentResult.notPresented - Could not show the popup (e.g., widget not mounted). The mixin will NOT call markDismissed().
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

The PresentumPopupSurfaceStateMixin automatically:
  1. Observes surface state via the required observer mixin
  2. Shows popups when active item changes
  3. Dismisses popups when active item becomes null
  4. Prevents duplicates with optional duplicate detection
  5. Resolves conflicts when multiple popups activate
  6. Manages queue for sequential popup display
  7. 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

Conflict strategies

Control what happens when a new popup activates while another is showing:

Ignore (default)

Keep showing the current popup, ignore new ones:
Use when: You want users to finish with one popup before seeing another.

Replace

Immediately dismiss the current popup and show the new one:
Use when: Newer popups have higher priority than older ones.

Queue

Queue new popups to show after the current one is dismissed:
Use when: You want to show all popups sequentially without skipping any.

Queuing behavior example

When using PopupConflictStrategy.queue:
With other strategies:
  • ignore: Items B and C are ignored while A shows
  • replace: 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:
See full source β†’
Notice markDismissed is not implemented. The default implementation handles it automatically.

Custom presentation

Fullscreen routes

Use Navigator.push instead of showDialog for fullscreen presentations:

Bottom sheets

Use showModalBottomSheet for bottom sheet presentations:

Custom pop behavior

Override pop() for custom navigation:

Dialog widgets

Create dialog widgets that access the item via InheritedPresentumItem:

Integration

Initialize the presentum and wrap your app with the popup host:
Then use it in your app:
The host watches for popup surface changes and shows dialogs automatically.
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

This ensures descendants can access the item and presentum instance:
Call markShown before displaying the popup:
Always check if the widget is mounted before showing popups:
Map dialog results to the appropriate enum value:
This ensures dismissal tracking is only recorded for non-conversions.
Always add PresentumActiveSurfaceItemObserverMixin before PresentumPopupSurfaceStateMixin:

Advanced: Custom dismissal tracking

Override markDismissed for custom logic:

When to use popup hosts

Use PresentumPopupSurfaceStateMixin 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
Don’t use popup hosts for non-modal UI like snackbars or banners. Use Surface observers instead for simpler, more appropriate handling.

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