ashlarui

Alert Dialog

A question that needs an answer before going on.

Alert Dialog, as it looks in an app Alert Dialog, as it looks in an app, dark
import 'package:flutter/widgets.dart';

import 'ui/alert_dialog.dart';
import 'ui/button.dart';

class AlertDialogDemo extends StatelessWidget {
  const AlertDialogDemo({super.key});

  @override
  Widget build(BuildContext context) => Button(
    variant: ButtonVariant.outline,
    onPressed: () async {
      final delete = await showAlertDialog(
        context,
        title: 'Delete the project?',
        description: 'This cannot be undone. The project and its files will be removed.',
        action: 'Delete',
        destructive: true,
      );
      if (delete) {
        // Delete it.
      }
    },
    child: const Text('Delete project'),
  );
}

Try its options

Each option its constructor takes, to change and see; the code below it is what you chose, to copy.

Alert Dialog with controls, as it looks in an app Alert Dialog with controls, as it looks in an app, dark

Add it

In your Flutter project:

$ ashlar add alert-dialog

It writes theme.dart, palette.dart, group.dart, pressable.dart, button.dart, dialog.dart and alert_dialog.dart into lib/ui, keeping any you already have. It needs dialog and button, which come with it.

Examples

Other ways to use it, each running live, with its code.

Destructive

For what cannot be undone.

Destructive, as it looks in an app Destructive, as it looks in an app, dark
// Destructive: For what cannot be undone.
import 'package:flutter/widgets.dart';

import 'ui/alert_dialog.dart';
import 'ui/button.dart';

class AlertDialogDestructiveExample extends StatelessWidget {
  const AlertDialogDestructiveExample({super.key});

  @override
  Widget build(BuildContext context) => Builder(
    builder: (context) => Button(
      variant: ButtonVariant.destructive,
      onPressed: () => showAlertDialog(
        context,
        title: 'Delete this project?',
        description: 'Its files and history go with it. This cannot be undone.',
        action: 'Delete',
        destructive: true,
      ),
      child: const Text('Delete project'),
    ),
  );
}

Small, with media

Narrow, an icon above.

Small, with media, as it looks in an app Small, with media, as it looks in an app, dark
// Small, with media: Narrow, an icon above.
import 'package:ashlar_icons/ashlar_icons.dart';
import 'package:flutter/widgets.dart';

import 'ui/alert_dialog.dart';
import 'ui/button.dart';

class AlertDialogSmallExample extends StatelessWidget {
  const AlertDialogSmallExample({super.key});

  @override
  Widget build(BuildContext context) => Builder(
    builder: (context) => Button(
      variant: ButtonVariant.outline,
      onPressed: () => showAlertDialog(
        context,
        title: 'Allow notifications?',
        description: 'We tell you when a delivery is on its way.',
        action: 'Allow',
        cancel: 'Not now',
        small: true,
        media: const AshlarIcon(AshlarIconsRoundedStroke.bell, size: 32),
      ),
      child: const Text('Turn on notifications'),
    ),
  );
}

API

Read from the source above: each parameter with its type, its default, and what it is for.

showAlertDialogfunction

Asks a question that needs an answer before going on: deleting, leaving with unsaved work. Unlike a Dialog it has no close button and a tap outside does nothing; Escape counts as cancel. Returns true for action, false for anything else.

Parameter Type Default What it is for
contextpositional BuildContext Where it is opened from: its theme and navigator come from here.
titlerequired String The question or warning.
description String? What happens next, in a line or two.
cancel String 'Cancel' The button that does not do it.
action String 'Continue' The button that does it.
destructive bool false Draws the action in the destructive colour, for what cannot be undone.
media Widget? An icon or picture above the title.
small bool false A narrow dialog, its buttons side by side.

AlertDialogclass

The panel showAlertDialog shows.

Parameter Type Default What it is for
titlerequired String The question or warning.
description String? What happens next, in a line or two.
cancel String 'Cancel' The button that does not.
action String 'Continue' The button that does it.
destructive bool false Draws the action in the destructive colour, for what cannot be undone.
media Widget? An icon above the title, on a muted circle.
small bool false Narrow and centred, the two buttons sharing the width.

Source

What lands in your app. It is yours from then on: change it as you would your own code, and ashlar add will not write over it.

lib/ui/alert_dialog.dart 110 lines
// Ashlar UI: Alert Dialog. Copied into your app by `ashlar add alert-dialog`; it is yours to
// change.

import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

import 'button.dart';
import 'dialog.dart';

/// Asks a question that needs an answer before going on: deleting, leaving with unsaved
/// work. Unlike a Dialog it has no close button and a tap outside does nothing; Escape
/// counts as [cancel]. Returns true for [action], false for anything else.
///
/// ```dart
/// if (await showAlertDialog(context, title: 'Delete the project?', description: 'This cannot be undone.', action: 'Delete', destructive: true)) {
///   delete();
/// }
/// ```
Future<bool> showAlertDialog(
  BuildContext context, {
  required String title,
  String? description,
  String cancel = 'Cancel',
  String action = 'Continue',
  bool destructive = false,
  Widget? media,
  bool small = false,
}) async =>
    await showAshlarDialog<bool>(
      context: context,
      dismissible: false,
      builder: (context) => AlertDialog(
        title: title,
        description: description,
        cancel: cancel,
        action: action,
        destructive: destructive,
        media: media,
        small: small,
      ),
    ) ??
    false;

/// The panel [showAlertDialog] shows.
class AlertDialog extends StatelessWidget {
  const AlertDialog({
    super.key,
    required this.title,
    this.description,
    this.cancel = 'Cancel',
    this.action = 'Continue',
    this.destructive = false,
    this.media,
    this.small = false,
  });

  /// The question or warning.
  final String title;

  /// What happens next, in a line or two.
  final String? description;

  /// The button that does not.
  final String cancel;

  /// The button that does it.
  final String action;

  /// Draws the action in the destructive colour, for what cannot be undone.
  final bool destructive;

  /// An icon above the title, on a muted circle.
  final Widget? media;

  /// Narrow and centred, the two buttons sharing the width.
  final bool small;

  @override
  Widget build(BuildContext context) => CallbackShortcuts(
    bindings: {
      const SingleActivator(LogicalKeyboardKey.escape): () =>
          Navigator.pop(context, false),
    },
    child: Focus(
      autofocus: true,
      child: Dialog(
        showClose: false,
        media: media,
        centered: small,
        maxWidth: small ? 320 : 512,
        title: Text(title),
        description: description == null ? null : Text(description!),
        actions: [
          Button(
            variant: ButtonVariant.outline,
            onPressed: () => Navigator.pop(context, false),
            child: Text(cancel),
          ),
          Button(
            variant: destructive
                ? ButtonVariant.destructive
                : ButtonVariant.primary,
            onPressed: () => Navigator.pop(context, true),
            child: Text(action),
          ),
        ],
      ),
    ),
  );
}