CNAlert class

A native alert dialog: UIAlertController on iOS, NSAlert on macOS.

On iOS this presents a real UIAlertController in UIAlertController.Style.alert, so the dialog is the system control: system typography and button dividers, the destructive red, VoiceOver, Dynamic Type, and — on iOS 26 — the Liquid Glass appearance. Because that appearance comes from the OS with no opt-in, the native path is used on every supported iOS version rather than being gated on iOS 26.

On macOS this presents a real NSAlert as a sheet on the app's window (beginSheetModal, never a blocking modal loop), on every supported macOS version:

  • Buttons follow AppKit's order: the CNAlertAction.isDefault action at the trailing edge, answering Return, the CNAlertAction.isCancel action next to it, answering Escape, then the others. Without an isDefault action the first action that is not cancel takes Return, unless it is destructive.
  • CNAlertAction.isDestructive sets NSButton.hasDestructiveAction.
  • CNAlertTextFields are NSTextFields (NSSecureTextField when obscured) in the alert's accessory view; the first one has the focus.
  • The alert shows the app icon, as every NSAlert does.

On every other platform the alert falls back to a CupertinoAlertDialog presented through showCupertinoDialog with barrierDismissible: false, matching UIKit — a .alert cannot be dismissed by tapping outside.

Basic usage

final result = await CNAlert.show<String>(
  context: context,
  title: 'Delete photo?',
  message: 'This cannot be undone.',
  actions: const [
    CNAlertAction(label: 'Cancel', isCancel: true),
    CNAlertAction(label: 'Delete', value: 'delete', isDestructive: true),
  ],
);

if (result?.value == 'delete') _deletePhoto();

With a text field

final result = await CNAlert.show<String>(
  context: context,
  title: 'Rename',
  textFields: const [
    CNAlertTextField(placeholder: 'Name', initialText: 'Untitled'),
  ],
  actions: const [
    CNAlertAction(label: 'Cancel', isCancel: true),
    CNAlertAction(label: 'Save', value: 'save', isDefault: true),
  ],
);

if (result?.value == 'save') rename(result!.textFields.first);

What this deliberately does not do

  • No custom content. UIAlertController has no public API for an arbitrary content view; the properties that would allow it (contentViewController, _headerContentViewController) are private.
  • No icons on actions. UIAlertAction has no public image property.
  • No severity. UIAlertController.severity is Mac Catalyst only.
  • No anchoring. An alert is always centred; anchoring to a source view is an action-sheet behaviour.

Properties

hashCode → int
The hash code for this object.
no setterinherited
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited

Methods

noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited

Static Methods

show<T>({required BuildContext context, required List<CNAlertAction<T>> actions, String? title, String? message, List<CNAlertTextField> textFields = const <CNAlertTextField>[], bool throwOnFailure = false, String? fontFamily, CNDialogTextStyle? titleStyle, CNDialogTextStyle? messageStyle, CNDialogTextStyle? actionStyle, TextDirection? textDirection}) → Future<CNAlertResult<T>?>
Presents the alert and resolves to what the user answered.