quick_popup_manager 0.0.3
quick_popup_manager: ^0.0.3 copied to clipboard
Global popup system for Flutter without BuildContext.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:quick_popup_manager/quick_popup_manager.dart';
void main() {
runApp(const MyApp());
}
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();
// Initialize after first frame
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_navigatorKey.currentState != null &&
_navigatorKey.currentState!.overlay != null) {
QuickPopupManager().initialize(_navigatorKey.currentState!.overlay!);
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Quick Popup Manager Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
// IMPORTANT: Add the navigator observer to enable global popups
navigatorObservers: [QuickPopupNavigatorObserver()],
navigatorKey: _navigatorKey,
builder: (context, child) {
// Ensure initialization on every build (synchronously)
final navigator = Navigator.maybeOf(context, rootNavigator: true);
if (navigator != null && navigator.overlay != null) {
QuickPopupManager().initialize(navigator.overlay!);
}
return child ?? const SizedBox();
},
home: const HomePage(),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Quick Popup Manager'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_buildSection(
title: '🍞 Toast Notifications',
description: 'No BuildContext needed!',
children: [
_buildButton(
'Success Toast (Top)',
Icons.check_circle,
Colors.green,
() => QuickPopupManager().showSuccessToast(
message: 'Operation completed successfully!',
title: 'Success',
position: PopupPosition.top,
),
),
_buildButton(
'Error Toast (Bottom)',
Icons.error,
Colors.red,
() => QuickPopupManager().showErrorToast(
message: 'Something went wrong!',
title: 'Error',
position: PopupPosition.bottom,
),
),
_buildButton(
'Warning Toast (Center)',
Icons.warning,
Colors.orange,
() => QuickPopupManager().showWarningToast(
message: 'Please check your input',
title: 'Warning',
position: PopupPosition.center,
),
),
_buildButton(
'Info Toast',
Icons.info,
Colors.blue,
() => QuickPopupManager().showInfoToast(
message: 'New features available',
title: 'Info',
),
),
_buildButton(
'Custom Toast',
Icons.star,
Colors.purple,
() => 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(),
),
),
],
),
const SizedBox(height: 24),
_buildSection(
title: '🍔 Snackbars',
description: 'Top/Bottom, Floating/Attached, Swipe to dismiss',
children: [
_buildButton(
'Bottom Snackbar (Floating)',
Icons.message,
Colors.blue,
() => QuickPopupManager().showSnackbar(
message: 'Item added to cart',
title: 'Success',
icon: Icons.shopping_cart,
position: SnackbarPosition.bottom,
behavior: SnackbarBehavior.floating,
),
),
_buildButton(
'Top Snackbar (Attached)',
Icons.arrow_upward,
Colors.teal,
() => QuickPopupManager().showSnackbar(
message: 'Message sent',
title: 'Notification',
subtitle: 'Your message has been delivered',
icon: Icons.send,
position: SnackbarPosition.top,
behavior: SnackbarBehavior.attached,
),
),
_buildButton(
'Snackbar with Preset',
Icons.palette,
Colors.pink,
() => QuickPopupManager().showSnackbar(
message: 'Profile updated',
title: 'Success',
icon: Icons.person,
style: PopupStyle.success(),
position: SnackbarPosition.bottom,
),
),
_buildButton(
'Replace Current Snackbar',
Icons.swap_horiz,
Colors.indigo,
() {
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,
);
});
},
),
],
),
const SizedBox(height: 24),
_buildSection(
title: '🚩 Banners',
description: 'Top/Bottom banners with auto-dismiss',
children: [
_buildButton(
'Top Banner',
Icons.flag,
Colors.orange,
() => QuickPopupManager().showBanner(
message: 'New update available!',
title: 'Update',
icon: Icons.system_update,
position: PopupPosition.top,
style: PopupStyle.info(),
),
),
_buildButton(
'Bottom Banner',
Icons.arrow_downward,
Colors.deepPurple,
() => QuickPopupManager().showBanner(
message: 'Connection restored',
title: 'Network',
icon: Icons.wifi,
position: PopupPosition.bottom,
style: PopupStyle.success(),
),
),
_buildButton(
'Error Banner',
Icons.error_outline,
Colors.red,
() => 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),
),
),
],
),
const SizedBox(height: 24),
_buildSection(
title: '💬 Dialogs',
description: 'Regular, Loading, Destructive',
children: [
_buildButton(
'Simple Dialog',
Icons.chat_bubble,
Colors.blue,
() => 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',
);
},
),
),
_buildButton(
'Loading Dialog',
Icons.hourglass_empty,
Colors.amber,
() async {
final loadingId = QuickPopupManager().showLoadingDialog(
message: 'Processing...',
);
// Simulate work
await Future.delayed(const Duration(seconds: 2));
QuickPopupManager().hideLoadingDialog(loadingId);
QuickPopupManager().showSuccessToast(
message: 'Processing complete!',
);
},
),
_buildButton(
'Destructive Dialog',
Icons.delete,
Colors.red,
() => QuickPopupManager().showDestructiveDialog(
title: 'Delete Item',
message: 'This action cannot be undone. Are you sure?',
onConfirm: () {
QuickPopupManager().showErrorToast(message: 'Item deleted');
},
confirmText: 'Delete',
),
),
_buildButton(
'Custom Dialog',
Icons.tune,
Colors.purple,
() => 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',
);
},
),
),
_buildButton(
'Non-Dismissible Dialog',
Icons.lock,
Colors.grey,
() => QuickPopupManager().showDialogPopup(
title: 'Important',
message: 'You cannot dismiss this by tapping outside',
barrierDismissible: false,
onConfirm: () {
QuickPopupManager().showInfoToast(message: 'Dialog closed');
},
),
),
],
),
const SizedBox(height: 24),
_buildSection(
title: '📄 Bottom Sheets',
description: 'Drag to dismiss, Custom heights, Keyboard-aware',
children: [
_buildButton(
'Half Height Sheet',
Icons.vertical_align_center,
Colors.teal,
() => QuickPopupManager().showBottomSheet(
title: 'Half Height Sheet',
message: 'This is a half-height bottom sheet',
height: 300,
onConfirm: () {
QuickPopupManager().showInfoToast(
message: 'Sheet confirmed',
);
},
),
),
_buildButton(
'Full Height Sheet',
Icons.fullscreen,
Colors.indigo,
() => 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),
),
),
),
),
_buildButton('Selection Sheet', Icons.list, Colors.pink, () {
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]}',
);
},
);
}),
_buildButton(
'Form Sheet',
Icons.edit,
Colors.blue,
() => 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!',
);
},
),
),
],
),
const SizedBox(height: 24),
_buildSection(
title: '🎨 Animation Examples',
description: 'Different animation styles',
children: [
_buildButton(
'Fade Animation',
Icons.visibility,
Colors.cyan,
() => QuickPopupManager().showToast(
message: 'Fade animation',
animation: const AnimationConfig.fade(),
),
),
_buildButton(
'Slide from Top',
Icons.arrow_downward,
Colors.blue,
() => QuickPopupManager().showToast(
message: 'Slides from top',
position: PopupPosition.top,
animation: const AnimationConfig.slideFromTop(),
),
),
_buildButton(
'Scale Animation',
Icons.zoom_in,
Colors.purple,
() => QuickPopupManager().showDialogPopup(
title: 'Scale Dialog',
message: 'This dialog uses scale animation',
animation: const AnimationConfig.scale(),
),
),
_buildButton(
'Spring Animation',
Icons.animation,
Colors.orange,
() => QuickPopupManager().showToast(
message: 'Spring animation!',
animation: const AnimationConfig.spring(),
),
),
],
),
const SizedBox(height: 24),
_buildSection(
title: '🎯 Preset Styles',
description: 'Success, Error, Warning, Info with themes',
children: [
_buildButton(
'Success Preset',
Icons.check_circle,
Colors.green,
() => QuickPopupManager().showSnackbar(
message: 'Operation successful',
style: PopupStyle.success(),
),
),
_buildButton(
'Error Preset',
Icons.error,
Colors.red,
() => QuickPopupManager().showSnackbar(
message: 'An error occurred',
style: PopupStyle.error(),
),
),
_buildButton(
'Warning Preset',
Icons.warning,
Colors.orange,
() => QuickPopupManager().showSnackbar(
message: 'Please be careful',
style: PopupStyle.warning(),
),
),
_buildButton(
'Info Preset',
Icons.info,
Colors.blue,
() => QuickPopupManager().showSnackbar(
message: 'Here is some information',
style: PopupStyle.info(),
),
),
],
),
const SizedBox(height: 32),
Center(
child: Text(
'All popups work without BuildContext!',
style: Theme.of(context).textTheme.bodySmall?.copyWith(
fontStyle: FontStyle.italic,
color: Colors.grey,
),
),
),
const SizedBox(height: 16),
],
),
);
}
Widget _buildSection({
required String title,
required String description,
required List<Widget> children,
}) {
return Card(
elevation: 2,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
const SizedBox(height: 4),
Text(
description,
style: TextStyle(fontSize: 12, color: Colors.grey[600]),
),
const SizedBox(height: 16),
...children,
],
),
),
);
}
Widget _buildButton(
String label,
IconData icon,
Color color,
VoidCallback onPressed,
) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: ElevatedButton.icon(
onPressed: onPressed,
icon: Icon(icon),
label: Text(label),
style: ElevatedButton.styleFrom(
backgroundColor: color,
foregroundColor: Colors.white,
minimumSize: const Size(double.infinity, 48),
),
),
);
}
}