ashlarui

Alert

A notice, warning or error in the page, read out when it appears.

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

import 'ui/alert.dart';

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

  @override
  Widget build(BuildContext context) => const SizedBox(
    width: 440,
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        Alert(
          icon: AshlarIcon(AshlarIconsRoundedStroke.circleCheck),
          title: Text('Payment received'),
          description: Text('KES 2,400 from 0712 345 678.'),
        ),
        SizedBox(height: 12),
        Alert(
          variant: AlertVariant.destructive,
          icon: AshlarIcon(AshlarIconsRoundedStroke.circleAlert),
          title: Text('The card was declined'),
          description: Text('Try another card or pay by mobile money.'),
        ),
      ],
    ),
  );
}

Try its options

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

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

Add it

In your Flutter project:

$ ashlar add alert

It writes theme.dart, palette.dart and alert.dart into lib/ui, keeping any you already have.

Examples

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

Something wrong

In the destructive colour, with an action.

Something wrong, as it looks in an app Something wrong, as it looks in an app, dark
// Something wrong: In the destructive colour, with an action.
import 'package:ashlar_icons/ashlar_icons.dart';
import 'package:flutter/widgets.dart';

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

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

  @override
  Widget build(BuildContext context) => SizedBox(
    width: 420,
    child: Alert(
      variant: AlertVariant.destructive,
      icon: const AshlarIcon(AshlarIconsRoundedStroke.circleAlert),
      title: const Text('Payment failed'),
      description: const Text('Your card was declined. Try another way to pay.'),
      action: Button(size: ButtonSize.sm, variant: ButtonVariant.outline, onPressed: () {}, child: const Text('Retry')),
    ),
  );
}

API

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

AlertVariantenum

ValueWhat it is for
primarySomething to know.
destructiveSomething that went wrong or needs fixing.

Alertclass

A message in the page that asks for attention: a notice, a warning, an error. A screen reader reads it out when it appears.

Parameter Type Default What it is for
titleWidget?The alert's title.
description Widget? A line under the title.
icon Widget? A 16px icon at the top left.
variant AlertVariant AlertVariant.primary Plain, or in the destructive colour for something wrong.
action Widget? A button at the top right.

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.dart 106 lines
// Ashlar UI: Alert. Copied into your app by `ashlar add alert`; it is yours to change.

import 'package:flutter/widgets.dart';

import 'theme.dart';

enum AlertVariant {
  /// Something to know.
  primary,

  /// Something that went wrong or needs fixing.
  destructive,
}

/// A message in the page that asks for attention: a notice, a warning, an error. A screen
/// reader reads it out when it appears.
///
/// ```dart
/// Alert(icon: AshlarIcon(AshlarIconsRoundedStroke.info), title: Text('Payment received'), description: Text('KES 2,400 from 0712 345 678.'))
/// Alert(variant: AlertVariant.destructive, icon: AshlarIcon(AshlarIconsRoundedStroke.circleAlert), title: Text('The card was declined'))
/// ```
class Alert extends StatelessWidget {
  const Alert({
    super.key,
    this.title,
    this.description,
    this.icon,
    this.variant = AlertVariant.primary,
    this.action,
  });

  /// The alert's title.
  final Widget? title;

  /// A line under the title.
  final Widget? description;

  /// A 16px icon at the top left.
  final Widget? icon;

  /// Plain, or in the destructive colour for something wrong.
  final AlertVariant variant;

  /// A button at the top right.
  final Widget? action;

  @override
  Widget build(BuildContext context) {
    final t = AshlarTheme.of(context);
    final c = t.colors;
    final destructive = variant == AlertVariant.destructive;
    final fg = destructive ? c.destructive : c.cardForeground;
    return Semantics(
      container: true,
      liveRegion: true,
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
        decoration: BoxDecoration(
          color: c.card,
          borderRadius: BorderRadius.circular(t.radiusLg),
          border: Border.all(color: c.border),
        ),
        child: IconTheme.merge(
          data: IconThemeData(color: fg, size: 16),
          child: Row(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              if (icon != null) ...[
                Padding(padding: const EdgeInsets.only(top: 2), child: icon),
                const SizedBox(width: 12),
              ],
              Expanded(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    if (title != null)
                      DefaultTextStyle.merge(
                        style: t.text.sm.copyWith(
                          color: fg,
                          fontWeight: FontWeight.w500,
                        ),
                        child: title!,
                      ),
                    if (title != null && description != null)
                      const SizedBox(height: 4),
                    if (description != null)
                      DefaultTextStyle.merge(
                        style: t.text.sm.copyWith(
                          color: destructive
                              ? c.destructive.withValues(alpha: .9)
                              : c.mutedForeground,
                        ),
                        child: description!,
                      ),
                  ],
                ),
              ),
              if (action != null) ...[const SizedBox(width: 12), action!],
            ],
          ),
        ),
      ),
    );
  }
}