Sign Up with Role Selector
Registration screen with role selection grid — Owner, Manager, or Employee
View component →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.
What's Included
Use Cases
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.
Copy this into your Flutter project. No external packages required.
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: () {})Registration screen with role selection grid — Owner, Manager, or Employee
View component →6-digit PIN input with auto-focus, auto-advance, and shake on error
View component →Clean email + password login with form validation and loading state
View component →