quick_popup_manager 0.1.0
quick_popup_manager: ^0.1.0 copied to clipboard
A powerful global popup system for Flutter. Show Toasts, Snackbars, Dialogs, Bottom Sheets, and Banners from anywhere - no BuildContext required! Features sequential queuing, haptic feedback, progress [...]
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:quick_popup_manager/quick_popup_manager.dart';
void main() {
runApp(const MyApp());
}
/// ------------------------------
/// APP ROOT
/// ------------------------------
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final overlay = _navigatorKey.currentState?.overlay;
if (overlay != null) QuickPopupManager().initialize(overlay);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Quick Popup Manager Demo',
debugShowCheckedModeBanner: false,
navigatorKey: _navigatorKey,
navigatorObservers: [QuickPopupNavigatorObserver()],
theme: ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
fontFamily: 'Google Sans',
scaffoldBackgroundColor: const Color(0xFF0F0F10),
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
),
appBarTheme: const AppBarTheme(
elevation: 0,
backgroundColor: Color(0xFF0F0F10),
foregroundColor: Colors.white,
titleTextStyle: TextStyle(
fontSize: 22,
fontWeight: FontWeight.bold,
fontFamily: 'Google Sans',
color: Colors.white,
),
),
),
builder: (context, child) {
final navigator = Navigator.maybeOf(context, rootNavigator: true);
if (navigator?.overlay != null) {
QuickPopupManager().initialize(navigator!.overlay!);
}
return child ?? const SizedBox();
},
home: const HomePage(),
);
}
}
/// ------------------------------
/// HOME PAGE
/// ------------------------------
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Quick Popup Manager')),
body: ListView(
padding: const EdgeInsets.all(20),
children: [
// 🍞 Toasts
_section(
title: '🍞 Toast Notifications',
subtitle: 'No BuildContext needed!',
children: [
ActionTile(
icon: Icons.check_circle,
color: Colors.green,
title: 'Success Toast (Top)',
onTap: () => QuickPopupManager().showSuccessToast(
title: 'Success',
message: 'Operation completed successfully!',
position: PopupPosition.top,
),
),
ActionTile(
icon: Icons.error,
color: Colors.red,
title: 'Error Toast (Bottom)',
onTap: () => QuickPopupManager().showErrorToast(
title: 'Error',
message: 'Something went wrong!',
position: PopupPosition.bottom,
),
),
ActionTile(
icon: Icons.warning,
color: Colors.orange,
title: 'Warning Toast (Center)',
onTap: () => QuickPopupManager().showWarningToast(
title: 'Warning',
message: 'Please check your input',
position: PopupPosition.center,
),
),
ActionTile(
icon: Icons.info,
color: Colors.blue,
title: 'Info Toast',
onTap: () => QuickPopupManager().showInfoToast(
message: 'New features available',
title: 'Info',
),
),
ActionTile(
icon: Icons.star,
color: Colors.purple,
title: 'Custom Toast',
onTap: () => QuickPopupManager().showToast(
message: 'Custom styled toast!',
title: 'Custom',
icon: Icons.star,
position: PopupPosition.top,
style: PopupStyle(
backgroundColor: Colors.purple,
borderRadius: 20,
titleStyle: const TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
),
messageStyle: const TextStyle(color: Colors.white),
),
animation: const AnimationConfig.spring(),
),
),
],
),
// 🍔 Snackbars
_section(
title: '🍔 Snackbars',
subtitle: 'Top/Bottom, Floating/Attached, Swipe to dismiss',
children: [
ActionTile(
icon: Icons.message,
color: Colors.blue,
title: 'Bottom Snackbar (Floating)',
onTap: () => QuickPopupManager().showSnackbar(
message: 'Item added to cart',
title: 'Success',
icon: Icons.shopping_cart,
position: SnackbarPosition.bottom,
behavior: SnackbarBehavior.floating,
),
),
ActionTile(
icon: Icons.arrow_upward,
color: Colors.teal,
title: 'Top Snackbar (Attached)',
onTap: () => QuickPopupManager().showSnackbar(
message: 'Message sent',
title: 'Notification',
subtitle: 'Your message has been delivered',
icon: Icons.send,
position: SnackbarPosition.top,
behavior: SnackbarBehavior.attached,
),
),
ActionTile(
icon: Icons.queue,
color: Colors.deepPurple,
title: 'Sequential Queue (3 in a row)',
onTap: () {
for (int i = 1; i <= 3; i++) {
QuickPopupManager().showSnackbar(
message: 'This is queued message $i',
title: 'Queue Demo',
icon: Icons.queue_play_next,
queue: true,
);
}
},
),
ActionTile(
icon: Icons.timelapse,
color: Colors.orange,
title: 'Progress Bar Snackbar',
onTap: () => QuickPopupManager().showSnackbar(
message: 'Undo action before time runs out!',
title: 'Undo',
icon: Icons.undo,
duration: const Duration(seconds: 5),
style: PopupStyle.warning().copyWith(
showProgressBar: true,
progressBarColor: Colors.white,
hapticFeedbackType: HapticFeedbackType.warning,
hapticTrigger: HapticTrigger.onShow,
),
),
),
ActionTile(
icon: Icons.palette,
color: Colors.pink,
title: 'Snackbar with Preset',
onTap: () => QuickPopupManager().showSnackbar(
message: 'Profile updated',
title: 'Success',
icon: Icons.person,
style: PopupStyle.success(),
position: SnackbarPosition.bottom,
),
),
ActionTile(
icon: Icons.swap_horiz,
color: Colors.indigo,
title: 'Replace Current Snackbar',
onTap: () {
QuickPopupManager().showSnackbar(
message: 'First snackbar',
position: SnackbarPosition.bottom,
);
Future.delayed(const Duration(milliseconds: 500), () {
QuickPopupManager().showSnackbar(
message: 'This replaced the previous one!',
replaceCurrent: true,
position: SnackbarPosition.bottom,
);
});
},
),
],
),
// 🚩 Banners
_section(
title: '🚩 Banners',
subtitle: 'Top/Bottom banners with auto-dismiss',
children: [
ActionTile(
icon: Icons.flag,
color: Colors.orange,
title: 'Top Banner',
onTap: () => QuickPopupManager().showBanner(
message: 'New update available!',
title: 'Update',
icon: Icons.system_update,
position: PopupPosition.top,
style: PopupStyle.info(),
),
),
ActionTile(
icon: Icons.arrow_downward,
color: Colors.deepPurple,
title: 'Bottom Banner',
onTap: () => QuickPopupManager().showBanner(
message: 'Connection restored',
title: 'Network',
icon: Icons.wifi,
position: PopupPosition.bottom,
style: PopupStyle.success(),
),
),
ActionTile(
icon: Icons.error_outline,
color: Colors.red,
title: 'Error Banner',
onTap: () => QuickPopupManager().showBanner(
message: 'Failed to sync data',
title: 'Sync Error',
icon: Icons.sync_problem,
position: PopupPosition.top,
style: PopupStyle.error(),
duration: const Duration(seconds: 4),
),
),
],
),
// 💬 Dialogs
_section(
title: '💬 Dialogs',
subtitle: 'Regular, Loading, Destructive, Custom',
children: [
ActionTile(
icon: Icons.chat_bubble,
color: Colors.blue,
title: 'Simple Dialog',
onTap: () => QuickPopupManager().showDialogPopup(
title: 'Confirm Action',
message: 'Are you sure you want to proceed?',
onConfirm: () {
QuickPopupManager().showSuccessToast(
message: 'Action confirmed!',
);
},
onCancel: () {
QuickPopupManager().showInfoToast(
message: 'Action cancelled',
);
},
),
),
ActionTile(
icon: Icons.hourglass_empty,
color: Colors.amber,
title: 'Loading Dialog',
onTap: () async {
final loadingId = QuickPopupManager().showLoadingDialog(
message: 'Processing...',
);
await Future.delayed(const Duration(seconds: 2));
QuickPopupManager().hideLoadingDialog(loadingId);
QuickPopupManager().showSuccessToast(
message: 'Processing complete!',
);
},
),
ActionTile(
icon: Icons.delete,
color: Colors.red,
title: 'Destructive Dialog',
onTap: () => QuickPopupManager().showDestructiveDialog(
title: 'Delete Item',
message: 'This action cannot be undone. Are you sure?',
onConfirm: () {
QuickPopupManager().showErrorToast(message: 'Item deleted');
},
confirmText: 'Delete',
),
),
ActionTile(
icon: Icons.tune,
color: Colors.purple,
title: 'Custom Dialog',
onTap: () => QuickPopupManager().showDialogPopup(
title: 'Custom Dialog',
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const TextField(
decoration: InputDecoration(
labelText: 'Enter your name',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
const Text('This is custom content!'),
],
),
style: PopupStyle(
backgroundColor: Colors.white,
borderRadius: 16,
),
onConfirm: () {
QuickPopupManager().showInfoToast(
message: 'Custom dialog confirmed',
);
},
),
),
ActionTile(
icon: Icons.lock,
color: Colors.grey,
title: 'Non-Dismissible Dialog',
onTap: () => QuickPopupManager().showDialogPopup(
title: 'Important',
message: 'You cannot dismiss this by tapping outside',
barrierDismissible: false,
onConfirm: () {
QuickPopupManager().showInfoToast(message: 'Dialog closed');
},
),
),
],
),
// 📄 Bottom Sheets
_section(
title: '📄 Bottom Sheets',
subtitle: 'Half, Full, Selection, Form',
children: [
ActionTile(
icon: Icons.vertical_align_center,
color: Colors.teal,
title: 'Half Height Sheet',
onTap: () => QuickPopupManager().showBottomSheet(
title: 'Half Height Sheet',
message: 'This is a half-height bottom sheet',
height: 300,
onConfirm: () {
QuickPopupManager().showInfoToast(
message: 'Sheet confirmed',
);
},
),
),
ActionTile(
icon: Icons.fullscreen,
color: Colors.indigo,
title: 'Full Height Sheet',
onTap: () => QuickPopupManager().showBottomSheet(
title: 'Full Height Sheet',
message: 'Drag down to dismiss',
isScrollControlled: true,
enableDrag: true,
content: ListView.builder(
itemCount: 20,
itemBuilder: (context, index) => ListTile(
title: Text('Item ${index + 1}'),
leading: const Icon(Icons.star),
),
),
),
),
ActionTile(
icon: Icons.list,
color: Colors.pink,
title: 'Selection Sheet',
onTap: () {
final options = [
'Option A',
'Option B',
'Option C',
'Option D',
];
int selectedIndex = 0;
QuickPopupManager().showBottomSheet(
title: 'Choose an option',
content: StatefulBuilder(
builder: (context, setState) {
return Column(
mainAxisSize: MainAxisSize.min,
children: options.asMap().entries.map((entry) {
final index = entry.key;
final option = entry.value;
return RadioListTile<int>(
title: Text(option),
value: index,
groupValue: selectedIndex,
onChanged: (value) {
if (value != null)
setState(() => selectedIndex = value);
},
);
}).toList(),
);
},
),
onConfirm: () {
QuickPopupManager().showSuccessToast(
message: 'Selected: ${options[selectedIndex]}',
);
},
);
},
),
ActionTile(
icon: Icons.edit,
color: Colors.blue,
title: 'Form Sheet',
onTap: () => QuickPopupManager().showBottomSheet(
title: 'Enter Details',
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
const TextField(
decoration: InputDecoration(
labelText: 'Name',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
const TextField(
decoration: InputDecoration(
labelText: 'Email',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
const TextField(
decoration: InputDecoration(
labelText: 'Phone',
border: OutlineInputBorder(),
),
),
],
),
isScrollControlled: true,
onConfirm: () {
QuickPopupManager().showSuccessToast(
message: 'Form submitted!',
);
},
),
),
],
),
// 🎨 Animation Examples
_section(
title: '🎨 Animation Examples',
subtitle: 'Fade, Slide, Scale, Spring',
children: [
ActionTile(
icon: Icons.visibility,
color: Colors.cyan,
title: 'Fade Animation',
onTap: () => QuickPopupManager().showToast(
message: 'Fade animation',
animation: const AnimationConfig.fade(),
),
),
ActionTile(
icon: Icons.arrow_downward,
color: Colors.blue,
title: 'Slide from Top',
onTap: () => QuickPopupManager().showToast(
message: 'Slides from top',
position: PopupPosition.top,
animation: const AnimationConfig.slideFromTop(),
),
),
ActionTile(
icon: Icons.zoom_in,
color: Colors.purple,
title: 'Scale Animation',
onTap: () => QuickPopupManager().showDialogPopup(
title: 'Scale Dialog',
message: 'This dialog uses scale animation',
animation: const AnimationConfig.scale(),
),
),
ActionTile(
icon: Icons.animation,
color: Colors.orange,
title: 'Spring Animation',
onTap: () => QuickPopupManager().showToast(
message: 'Spring animation!',
animation: const AnimationConfig.spring(),
),
),
],
),
// 🎯 Preset Styles
_section(
title: '🎯 Preset Styles',
subtitle: 'Success, Error, Warning, Info with themes',
children: [
ActionTile(
icon: Icons.check_circle,
color: Colors.green,
title: 'Success Preset',
onTap: () => QuickPopupManager().showSnackbar(
message: 'Operation successful',
style: PopupStyle.success(),
),
),
ActionTile(
icon: Icons.error,
color: Colors.red,
title: 'Error Preset',
onTap: () => QuickPopupManager().showSnackbar(
message: 'An error occurred',
style: PopupStyle.error(),
),
),
ActionTile(
icon: Icons.warning,
color: Colors.orange,
title: 'Warning Preset',
onTap: () => QuickPopupManager().showSnackbar(
message: 'Please be careful',
style: PopupStyle.warning(),
),
),
ActionTile(
icon: Icons.info,
color: Colors.blue,
title: 'Info Preset',
onTap: () => QuickPopupManager().showSnackbar(
message: 'Here is some information',
style: PopupStyle.info(),
),
),
],
),
const SizedBox(height: 32),
const Center(
child: Text(
'✨ All popups work without BuildContext!',
style: TextStyle(
fontSize: 13,
color: Colors.grey,
fontStyle: FontStyle.italic,
),
),
),
const SizedBox(height: 24),
],
),
);
}
/// ------------------------------
/// SECTION COMPONENT
/// ------------------------------
Widget _section({
required String title,
required String subtitle,
required List<Widget> children,
}) {
return Padding(
padding: const EdgeInsets.only(bottom: 28),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
fontFamily: 'Google Sans',
color: Colors.white,
),
),
const SizedBox(height: 4),
Text(
subtitle,
style: const TextStyle(
fontSize: 13,
color: Colors.white60,
fontFamily: 'Google Sans',
),
),
const SizedBox(height: 16),
...children,
],
),
);
}
}
/// ------------------------------
/// ACTION TILE COMPONENT
/// ------------------------------
class ActionTile extends StatelessWidget {
final IconData icon;
final Color color;
final String title;
final VoidCallback onTap;
const ActionTile({
super.key,
required this.icon,
required this.color,
required this.title,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(bottom: 12),
child: Material(
elevation: 0,
color: const Color(0xFF1C1C1E),
borderRadius: BorderRadius.circular(16),
child: InkWell(
borderRadius: BorderRadius.circular(16),
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
CircleAvatar(
radius: 22,
backgroundColor: color.withOpacity(0.15),
child: Icon(icon, color: color),
),
const SizedBox(width: 16),
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
fontFamily: 'Google Sans',
color: Colors.white,
),
),
),
const Icon(
Icons.arrow_forward_ios,
size: 18,
color: Colors.grey,
),
],
),
),
),
),
);
}
}