UI ELEMENTS

Alert Widget

A fully customizable Flutter Alert widget supporting 5 styles (Soft, Outlined, Filled, With Title, With Actions) and 4 types (Info, Success, Warning, Danger). Built with a single reusable StatefulWidget driven by enums — swap styles and types with one parameter change. Includes dismiss functionality out of the box.

alertnotificationfeedback
Preview20 variants
This is a info alert — check it out!
This is a success alert — check it out!
This is a warning alert — check it out!
This is a danger alert — check it out!

What's Included

  • Soft style — light colored background with matching icon and text
  • Outlined style — white background with colored border
  • Filled style — solid colored background with white text
  • With Title style — bold title line + description text below
  • With Actions style — outline + filled action buttons on the right
  • Dismiss functionality with animated hide using setState

Use Cases

  • Form validation feedback — show success or error after submission
  • API error messages — display danger alerts when requests fail
  • Feature announcements — info alerts for new app updates
  • Warning before destructive actions — warn users before delete or logout

PRO TIP

Use the AlertStyle.withActions variant for critical flows like payment confirmation or data deletion. Pass an onAction callback to handle the primary action, and wrap AlertWidget in an AnimatedSwitcher for a smooth fade-out when the user dismisses it — far better UX than an abrupt disappear.

Complete Dart Code

Copy this into your Flutter project. No external packages required.

dart
import 'package:flutter/material.dart';

/// Flutter Alert Widget — All Variants
/// Copy the AlertWidget class and use AlertType enum to switch variants.
/// rohansurve.in/flutterkit/alert-widget

enum AlertType { info, success, warning, danger }
enum AlertStyle { soft, outlined, filled, withTitle, withActions }

class AlertWidget extends StatefulWidget {
  final AlertType type;
  final AlertStyle style;
  final String message;
  final String? title;
  final VoidCallback? onAction;
  final String? actionLabel;
  final bool dismissible;

  const AlertWidget({
    super.key,
    required this.type,
    required this.message,
    this.style = AlertStyle.soft,
    this.title,
    this.onAction,
    this.actionLabel,
    this.dismissible = true,
  });

  @override
  State<AlertWidget> createState() => _AlertWidgetState();
}

class _AlertWidgetState extends State<AlertWidget> {
  bool _visible = true;

  AlertConfig get _config {
    switch (widget.type) {
      case AlertType.info:
        return AlertConfig(
          icon: Icons.info_outline_rounded,
          softBg: const Color(0xFFEFF6FF),
          softText: const Color(0xFF1D4ED8),
          outlinedBorder: const Color(0xFFBFDBFE),
          filledBg: const Color(0xFF3B82F6),
          filledText: Colors.white,
          label: 'Info',
        );
      case AlertType.success:
        return AlertConfig(
          icon: Icons.check_circle_outline_rounded,
          softBg: const Color(0xFFF0FDF4),
          softText: const Color(0xFF15803D),
          outlinedBorder: const Color(0xFFBBF7D0),
          filledBg: const Color(0xFF22C55E),
          filledText: Colors.white,
          label: 'Success',
        );
      case AlertType.warning:
        return AlertConfig(
          icon: Icons.warning_amber_rounded,
          softBg: const Color(0xFFFFFBEB),
          softText: const Color(0xFFB45309),
          outlinedBorder: const Color(0xFFFDE68A),
          filledBg: const Color(0xFFF59E0B),
          filledText: const Color(0xFF1C1917),
          label: 'Warning',
        );
      case AlertType.danger:
        return AlertConfig(
          icon: Icons.error_outline_rounded,
          softBg: const Color(0xFFFFF1F2),
          softText: const Color(0xFFBE123C),
          outlinedBorder: const Color(0xFFFECACA),
          filledBg: const Color(0xFFEF4444),
          filledText: Colors.white,
          label: 'Danger',
        );
    }
  }

  @override
  Widget build(BuildContext context) {
    if (!_visible) return const SizedBox.shrink();
    final config = _config;

    switch (widget.style) {
      case AlertStyle.soft:
        return _buildSoft(config);
      case AlertStyle.outlined:
        return _buildOutlined(config);
      case AlertStyle.filled:
        return _buildFilled(config);
      case AlertStyle.withTitle:
        return _buildWithTitle(config);
      case AlertStyle.withActions:
        return _buildWithActions(config);
    }
  }

  Widget _buildSoft(AlertConfig config) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(
        color: config.softBg,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        children: [
          Icon(config.icon, color: config.softText, size: 18),
          const SizedBox(width: 10),
          Expanded(
            child: Text(widget.message,
                style: TextStyle(color: config.softText, fontSize: 14)),
          ),
          if (widget.dismissible)
            GestureDetector(
              onTap: () => setState(() => _visible = false),
              child: Icon(Icons.close, color: config.softText.withOpacity(0.6), size: 16),
            ),
        ],
      ),
    );
  }

  Widget _buildOutlined(AlertConfig config) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(
        color: Colors.white,
        border: Border.all(color: config.outlinedBorder),
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        children: [
          Icon(config.icon, color: config.softText, size: 18),
          const SizedBox(width: 10),
          Expanded(
            child: Text(widget.message,
                style: TextStyle(color: config.softText, fontSize: 14)),
          ),
          if (widget.dismissible)
            GestureDetector(
              onTap: () => setState(() => _visible = false),
              child: Icon(Icons.close, color: config.softText.withOpacity(0.6), size: 16),
            ),
        ],
      ),
    );
  }

  Widget _buildFilled(AlertConfig config) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(
        color: config.filledBg,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        children: [
          Icon(config.icon, color: config.filledText, size: 18),
          const SizedBox(width: 10),
          Expanded(
            child: Text(widget.message,
                style: TextStyle(color: config.filledText, fontSize: 14)),
          ),
          if (widget.dismissible)
            GestureDetector(
              onTap: () => setState(() => _visible = false),
              child: Icon(Icons.close, color: config.filledText.withOpacity(0.7), size: 16),
            ),
        ],
      ),
    );
  }

  Widget _buildWithTitle(AlertConfig config) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(
        color: config.softBg,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Icon(config.icon, color: config.softText, size: 18),
          const SizedBox(width: 10),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(widget.title ?? config.label,
                    style: TextStyle(
                        color: config.softText,
                        fontSize: 14,
                        fontWeight: FontWeight.w600)),
                const SizedBox(height: 2),
                Text(widget.message,
                    style: TextStyle(
                        color: config.softText.withOpacity(0.8), fontSize: 13)),
              ],
            ),
          ),
          if (widget.dismissible)
            GestureDetector(
              onTap: () => setState(() => _visible = false),
              child: Icon(Icons.close, color: config.softText.withOpacity(0.6), size: 16),
            ),
        ],
      ),
    );
  }

  Widget _buildWithActions(AlertConfig config) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      decoration: BoxDecoration(
        color: config.filledBg,
        borderRadius: BorderRadius.circular(8),
      ),
      child: Row(
        children: [
          Icon(config.icon, color: config.filledText, size: 18),
          const SizedBox(width: 10),
          Expanded(
            child: Text(widget.message,
                style: TextStyle(color: config.filledText, fontSize: 14)),
          ),
          const SizedBox(width: 8),
          OutlinedButton(
            onPressed: () {},
            style: OutlinedButton.styleFrom(
              foregroundColor: config.filledText,
              side: BorderSide(color: config.filledText.withOpacity(0.5)),
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
              minimumSize: Size.zero,
              tapTargetSize: MaterialTapTargetSize.shrinkWrap,
            ),
            child: const Text('Large', style: TextStyle(fontSize: 12)),
          ),
          const SizedBox(width: 6),
          ElevatedButton(
            onPressed: widget.onAction,
            style: ElevatedButton.styleFrom(
              backgroundColor: config.filledText,
              foregroundColor: config.filledBg,
              padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
              minimumSize: Size.zero,
              tapTargetSize: MaterialTapTargetSize.shrinkWrap,
              elevation: 0,
            ),
            child: Text(widget.actionLabel ?? 'Action',
                style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600)),
          ),
        ],
      ),
    );
  }
}

class AlertConfig {
  final IconData icon;
  final Color softBg;
  final Color softText;
  final Color outlinedBorder;
  final Color filledBg;
  final Color filledText;
  final String label;

  const AlertConfig({
    required this.icon,
    required this.softBg,
    required this.softText,
    required this.outlinedBorder,
    required this.filledBg,
    required this.filledText,
    required this.label,
  });
}

// ─── USAGE EXAMPLES ──────────────────────────────────────────────

// Soft Info Alert
// AlertWidget(type: AlertType.info, message: 'This is an info alert')

// Outlined Success Alert
// AlertWidget(type: AlertType.success, style: AlertStyle.outlined, message: 'Operation successful!')

// Filled Warning Alert
// AlertWidget(type: AlertType.warning, style: AlertStyle.filled, message: 'Please review before continuing')

// With Title Danger Alert
// AlertWidget(type: AlertType.danger, style: AlertStyle.withTitle, title: 'Error', message: 'Something went wrong')

// With Actions Info Alert
// AlertWidget(type: AlertType.info, style: AlertStyle.withActions, message: 'New update available', actionLabel: 'Update', onAction: () {})

How to use this widget

  1. 1Copy the Dart code above
  2. 2Create a new file — alert_widget.dart in your Flutter project
  3. 3Use AlertWidget(type: AlertType.info, message: 'Your message') — change type and style parameters to switch variants

Related components

Auth & Onboarding

Sign Up with Role Selector

Registration screen with role selection grid — Owner, Manager, or Employee

View component →
Auth & Onboarding

OTP Input Field

6-digit PIN input with auto-focus, auto-advance, and shake on error

View component →
Auth & Onboarding

Login Screen

Clean email + password login with form validation and loading state

View component →