super_overlay 0.1.2 copy "super_overlay: ^0.1.2" to clipboard
super_overlay: ^0.1.2 copied to clipboard

Self-managed OverlayEntry dialogs, toasts, popups, loading indicators, and notifications for Flutter.

SuperOverlay #

SuperOverlay is a Flutter overlay package built on a self-managed OverlayEntry tree. It provides custom dialogs, loading indicators, toasts, target-attached popups, highlighted masks, notifications, route binding, and back-button handling without requiring a package-owned Navigator key.

Install #

dependencies:
  super_overlay: ^0.1.2

Initialize #

Add SuperOverlayInit.init() to MaterialApp.builder and register SuperOverlayInit.observer so page-bound overlays can react to route changes.

MaterialApp(
  builder: SuperOverlayInit.init(),
  navigatorObservers: [SuperOverlayInit.observer],
  home: const AppHome(),
);

The package entrypoint exports the intended consumer API, including configuration classes, public enums, SuperOverlayController, AnimationParam, popup geometry extension types, and default feedback builder types.

Requirements #

SuperOverlay requires Dart >=3.7.0 <4.0.0 and Flutter >=3.29.0.

Global Configuration #

Tune defaults before showing overlays. Per-call builder methods still override the global defaults.

SuperOverlay.config.custom = const CustomDialogConfig(
  animationTime: Duration(milliseconds: 160),
  debounce: true,
  debounceTime: Duration(milliseconds: 500),
  bindPage: true,
);

SuperOverlay.config.toast = const ToastConfig(
  displayTime: Duration(seconds: 2),
  debounce: true,
);

Custom Overlay #

final result = await SuperOverlay.show(
  builder: (_) => const MyDialog(),
)
    .withTag('profile')
    .withMask(dismissible: true)
    .withBack(type: BackType.normal)
    .fire<String>();

await SuperOverlay.dismiss(
  status: DismissStatus.auto,
  tag: 'profile',
  result: 'closed',
);

Permanent overlays are skipped by normal untagged dismiss calls and by allDialog/allCustom cleanup. Close them explicitly with their tag and force: true when the owning flow is finished.

SuperOverlay.show(
  builder: (_) => const BlockingDialog(),
).withTag('blocking-flow').withPermanent().fire<void>();

await SuperOverlay.dismiss(tag: 'blocking-flow', force: true);

Loading, Toast, Popup, And Notify #

SuperOverlay.showLoading(msg: 'Loading...').fire();
await SuperOverlay.dismiss(status: DismissStatus.loading);

await SuperOverlay.showToast('Saved').fire();

await SuperOverlay.showPopup(
  targetContext: targetContext,
  builder: (_) => const PopupMenu(),
).withHighlight().fire();

await SuperOverlay.showNotify(
  msg: 'Done',
  type: NotifyType.success,
).fire();

Popups can attach to a widget context, a custom target point derived from that context, or a target point without a target widget.

await SuperOverlay.showPopup(
  targetContext: targetContext,
  builder: (_) => const PopupMenu(),
)
    .withTargetPoint((targetOffset, targetSize) {
      return targetOffset + Offset(0, targetSize.height + 8);
    })
    .fire<void>();

await SuperOverlay.showPopup(
  builder: (_) => const PopupMenu(),
).withTargetPoint((_, _) => const Offset(240, 180)).fire<void>();

Corner popups can align inside the target edge, centered on the target edge, or outside the target edge. Popup geometry is clamped when it would leave the screen.

await SuperOverlay.showPopup(
  targetContext: targetContext,
  builder: (_) => const PopupMenu(),
)
    .withAlignment(Alignment.bottomLeft)
    .withAlignmentMode(PopupAlignmentMode.center)
    .fire<void>();

Replacement and adjustment hooks can react to measured target and popup geometry.

await SuperOverlay.showPopup(
  targetContext: targetContext,
  builder: (_) => const PopupMenu(),
)
    .withReplacement((info) {
      return PopupMenu(anchor: info.targetOffset, size: info.popupSize);
    })
    .withAdjustment((info) {
      return const PopupAdjustment(alignment: Alignment.centerRight);
    })
    .withScaleOrigin((popupSize) => Offset(popupSize.width, 0))
    .fire<void>();

Mask ignore areas apply only to the popup mask layer, so uncovered app chrome can continue receiving input.

SuperOverlay.config.attach = const AttachDialogConfig(
  nonAnimationTypes: [NonAnimationType.highlightMask],
);

await SuperOverlay.showPopup(
  targetContext: targetContext,
  builder: (_) => const PopupMenu(),
)
    .withHighlight()
    .withMaskIgnoreArea(const Rect.fromLTRB(0, 0, 0, 80))
    .fire<void>();

Default Feedback Builders #

Applications can set default loading, toast, and notify rendering during initialization. Per-call builders still take precedence.

MaterialApp(
  builder: SuperOverlayInit.init(
    toastBuilder: (message) => Text('Toast: $message'),
    loadingBuilder: (message) => Text('Loading: $message'),
    notifyStyle: NotifyStyle(
      successBuilder: (message) => Text('Success: $message'),
    ),
  ),
  navigatorObservers: [SuperOverlayInit.observer],
  home: const AppHome(),
);

Await Semantics #

By default, custom overlays, popups, loading indicators, and notifications complete their fire() future after dismissal. Toasts complete after scheduling by default so lightweight feedback calls do not pretend to represent dismissal.

Use .withAwait(...) when a call site needs different completion timing:

await SuperOverlay.show(
  builder: (_) => const MyDialog(),
).withAwait(AwaitCompletion.appear).fire<void>();

await SuperOverlay.showToast('Saved')
    .withAwait(AwaitCompletion.dismiss)
    .fire<void>();

AwaitCompletion.dismiss is the only mode whose result value is meaningful.

Network State And Empty Pages #

super_overlay keeps empty and error pages in the application layer. Use overlay loading for short blocking requests, toast or notify for lightweight feedback, and render empty/error states inside the page that owns the data.

The example app includes a Network State Demo that follows this split:

SuperOverlay.showLoading(msg: 'Loading...').fire();
try {
  final items = await loadItems();
  // Render list, empty page, or error page in your own widget tree.
} finally {
  await SuperOverlay.dismiss(status: DismissStatus.loading);
}

Route And Widget Binding #

Custom overlays and popups bind to the current page by default. When a new route covers that page, the bound overlay hides; when the page returns, it reappears. When the bound route is removed, the overlay is removed as well.

await SuperOverlay.show(
  builder: (_) => const Text('Bound'),
).bindPage().fire();

await SuperOverlay.show(
  builder: (_) => const Text('Follows target'),
).bindWidget(targetContext).fire();

Compatibility With Reference Ideas #

SuperOverlay is a breaking rewrite, not a compatibility layer for another package or the initial route-based API. The fluent builder API is the intended public API, reference-project names are not copied into current-project APIs, and useSystem remains out of scope unless a later requirement proves it is needed. See doc/reference-comparison.md for the reference comparison used to scope this rewrite.

Migration Notes #

This rewrite is not compatible with the initial route-based API. The package no longer exposes SuperOverlay.navigatorKey, show(content:), showToast(msg:), or showPopup(content:).

The project is MIT licensed. THIRD_PARTY_NOTICES.md records the MIT notice for reference implementation ideas used during the rewrite.

0
likes
150
points
306
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Self-managed OverlayEntry dialogs, toasts, popups, loading indicators, and notifications for Flutter.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on super_overlay