π Flutter Easy Messages
A powerful, elegant, and highly customizable Flutter package for displaying toast notifications and snackbars. Built for both simple notifications and complex enterprise scenarios like API error handling, file operations, and request tracking.
β¨ Features Overview
π― Core Features
- π Toast Notifications - Overlay-based notifications with smooth animations and multiple styling options
- π± Responsive Snackbars - Automatic sizing that adapts to mobile, tablet, and desktop screens
- π¨ Message Types - 4 pre-styled message types: Error, Success, Info, Warning
- π 9 Position Options - Complete positioning flexibility: top/center/bottom with left/center/right alignment
- β‘ Smooth Animations - Customizable entry, exit, and pulse animations with preset speeds
- π Queue & Replace Modes - Control how multiple messages are handled (queue sequentially or replace)
- π² Full Responsiveness - Automatic layout adjustments for all screen sizes and orientations
- βΏ Accessibility - Screen reader integration with semantic labels for inclusive apps
- π― Smart Config - Global defaults with per-message overrides for maximum flexibility
π Advanced Features (Enterprise-Ready)
- π Action Buttons - Add interactive retry, cancel, or custom buttons to toasts
- π Expandable Error Details - Display detailed error information that users can expand on demand
- β³ Persistent Toasts - Long-running operation toasts that don't auto-dismiss
- π Dismissible Toasts - User-controlled dismissal with intuitive tap-to-close gesture
- π Request ID Tracking - Track and manage toasts by request ID for API correlation
- π Lifecycle Callbacks - React to toast lifecycle events (onShown, onDismissed)
- π Custom Text Styling - Full control over font size, weight, family, and alignment
- π Context-Free Toasts - Show toasts anywhere in your app without BuildContext
- π― Deduplication - Built-in prevention of duplicate messages
- π³ Haptic Feedback (Vibration) - Optional iOS/Android vibration tied to message type, zero dependencies, zero permissions
π¬ Demo
Message Types & Colors
|
Snackbars
|
Toast Positions (9 Options)
|
Custom Styling & Icons
|
Animations & Durations
|
π Quick Start (2 Steps!)
Step 1: Add to Dependencies
# pubspec.yaml
dependencies:
flutter_easy_messages: ^0.3.0
Then run:
flutter pub get
Step 2: Use It!
import 'package:flutter_easy_messages/flutter_easy_messages.dart';
// Inside a widget with BuildContext
showAppToast(
'Success!',
context: context,
messageType: MessageType.success,
);
That's it! π No complex setup or configuration needed to get started.
π Complete Usage Guide
π Basic Toast Notifications
Using Message Types
// Success Toast - Green with checkmark
showAppToast(
'Operation completed successfully!',
context: context,
messageType: MessageType.success,
);
// Error Toast - Red with error icon
showAppToast(
'Something went wrong',
context: context,
messageType: MessageType.error,
);
// Info Toast - Blue with info icon
showAppToast(
'Here is some useful information',
context: context,
messageType: MessageType.info,
);
// Warning Toast - Orange with warning icon
showAppToast(
'Please be careful with this action',
context: context,
messageType: MessageType.warning,
);
Custom Styling
showAppToast(
'Custom styled notification',
context: context,
backgroundColor: Colors.purple,
duration: Duration(seconds: 3),
position: MessagePosition.topCenter,
borderRadius: 20,
icon: Icon(Icons.favorite, color: Colors.white),
fontSize: 16,
fontWeight: FontWeight.bold,
fontFamily: 'Roboto',
maxLines: 2,
offset: Offset(0, 10),
);
Position Your Toasts (9 Options)
// Top positions
MessagePosition.topLeft // βοΈ Top-left corner
MessagePosition.topCenter // β¬οΈ Top center
MessagePosition.topRight // βοΈ Top-right corner
// Center positions (vertically centered)
MessagePosition.centerLeft // β¬
οΈ Center-left
MessagePosition.center // β Center screen
MessagePosition.centerRight // β‘οΈ Center-right
// Bottom positions
MessagePosition.bottomLeft // βοΈ Bottom-left corner
MessagePosition.bottomCenter // β¬οΈ Bottom center (default)
MessagePosition.bottomRight // βοΈ Bottom-right corner
Real example:
showAppToast(
'Important notification',
context: context,
messageType: MessageType.info,
position: MessagePosition.topRight,
duration: Duration(seconds: 4),
);
Managing Multiple Messages
// Replace Mode (default) - Only one toast visible at a time
for (int i = 1; i <= 3; i++) {
showAppToast(
'Message $i',
context: context,
behavior: MessageBehavior.replace,
);
}
// Result: Only 'Message 3' is shown
// Queue Mode - Messages shown sequentially
for (int i = 1; i <= 3; i++) {
showAppToast(
'Message $i',
context: context,
behavior: MessageBehavior.queue,
);
}
// Result: All 3 messages shown in order
π Snackbars
// Simple snackbar notification
showAppSnackBar(
'This is a snackbar message',
context: context,
messageType: MessageType.success,
);
// Snackbar with custom styling
showAppSnackBar(
'Customized snackbar',
context: context,
messageType: MessageType.info,
duration: Duration(seconds: 4),
fontSize: 16,
fontWeight: FontWeight.w600,
);
π Context-Free Toasts (No BuildContext Needed!)
Perfect for showing toasts from services, utilities, and API calls without needing a BuildContext.
Setup (One-Time in main.dart)
import 'package:flutter_easy_messages/flutter_easy_messages.dart';
void main() {
// Create a navigator key
final navigatorKey = GlobalKey<NavigatorState>();
// Set the navigator key FIRST
EasyMessageConfig.setNavigatorKey(navigatorKey);
// Optional: Configure global defaults
EasyMessageConfig.configure(
toastDuration: Duration(seconds: 2),
borderRadius: 12,
toastPosition: MessagePosition.bottomCenter,
enablePulse: true,
);
runApp(MyApp(navigatorKey: navigatorKey));
}
class MyApp extends StatelessWidget {
final GlobalKey<NavigatorState> navigatorKey;
const MyApp({required this.navigatorKey, super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey, // β Pass the same key
home: HomeScreen(),
);
}
}
Usage (Show Toasts Anywhere!)
// No context required!
showAppToast('Welcome back!', messageType: MessageType.success);
// In services
class AuthService {
void logout() {
showAppToast('Logged out successfully', messageType: MessageType.info);
}
}
// In utility functions
void handleError(String error) {
showAppToast(error, messageType: MessageType.error);
}
// In API calls
Future<void> fetchData() async {
try {
final data = await api.getData();
} catch (e) {
showAppToast('Failed to load data', messageType: MessageType.error);
}
}
π― Advanced Features (Enterprise-Grade)
π Action Buttons - Add Interactivity
Add retry, cancel, or custom action buttons to your toasts.
showAppToast(
'Failed to upload document.pdf',
context: context,
messageType: MessageType.error,
duration: Duration(seconds: 10),
actions: [
ToastAction(
label: 'Retry',
color: Colors.green,
textColor: Colors.white,
onPressed: () {
retryUpload();
},
),
ToastAction(
label: 'Cancel',
color: Colors.red,
textColor: Colors.white,
onPressed: () {
cancelOperation();
},
),
],
);
π Error Details - Expandable Information
Display detailed error information that users can expand when needed.
showAppToast(
'β API Request Failed',
context: context,
messageType: MessageType.error,
duration: Duration(seconds: 8),
errorDetails:
'Status Code: 500\n'
'Endpoint: /api/v1/upload\n'
'Error: Internal Server Error\n'
'Request ID: REQ-123456789',
);
User sees: β API Request Failed [Show details]
Tapping reveals full error information.
β³ Persistent Toasts - For Long Operations
Perfect for upload, download, or processing notifications that shouldn't auto-dismiss.
showAppToast(
'β³ Processing PDF file...',
context: context,
messageType: MessageType.info,
isPersistent: true, // Won't auto-dismiss
dismissible: true, // User can tap to close
duration: Duration(seconds: 999),
requestId: 'processing_pdf_001',
onShown: () {
// Called when toast appears
startProcessing();
},
onDismissed: () {
// Called when dismissed
cleanup();
},
);
// Later: Programmatically clear the toast
ToastManager.clearByRequestId('processing_pdf_001');
π Dismissible Toasts
Enable users to close notifications with a tap.
showAppToast(
'π Tap anywhere on this toast to close it',
context: context,
messageType: MessageType.warning,
dismissible: true,
duration: Duration(seconds: 5),
);
π Request ID Tracking
Track and manage toasts by request IDβideal for API request correlation and preventing duplicates.
// Show toast for a specific request
showAppToast(
'Processing order...',
context: context,
messageType: MessageType.info,
isPersistent: true,
requestId: 'order_checkout_001',
);
// Check how many toasts exist for this request
int count = ToastManager.getToastCountByRequestId('order_checkout_001');
// Clear all toasts for this request
await ToastManager.clearByRequestId('order_checkout_001');
// Clear everything
await ToastManager.clearAll();
π Lifecycle Callbacks
React to toast eventsβperfect for analytics, logging, and state management.
showAppToast(
'Processing...',
context: context,
messageType: MessageType.info,
isPersistent: true,
onShown: () {
// Called when toast appears on screen
analytics.logEvent('notification_shown', {'message': 'Processing'});
startTimer();
},
onDismissed: () {
// Called when toast is dismissed
analytics.logEvent('notification_dismissed', {'message': 'Processing'});
stopTimer();
refreshUI();
},
);
π³ Haptic Feedback (Vibration)
Add a tactile pulse to your toasts and snackbars. Uses Flutter's built-in HapticFeedback, so there's no extra dependency and no Android VIBRATE permission required. On platforms without haptic hardware (web, desktop, simulators) the call is a silent no-op.
Quick Enable (per-call)
showAppToast(
'Saved!',
context: context,
messageType: MessageType.success,
enableVibration: true, // π fires a haptic pulse
);
When enableVibration: true and no hapticType is given, the haptic is automatically derived from the MessageType:
| MessageType | Haptic |
|---|---|
error |
HapticFeedbackType.heavyImpact |
warning |
HapticFeedbackType.mediumImpact |
success |
HapticFeedbackType.selectionClick |
info / unspecified |
HapticFeedbackType.lightImpact |
Pick a Specific Haptic Variant
showAppToast(
'Boom',
context: context,
enableVibration: true,
hapticType: HapticFeedbackType.vibrate, // any of the 5 variants
);
Available variants: lightImpact, mediumImpact, heavyImpact, selectionClick, vibrate.
Enable Globally (so every message vibrates)
EasyMessageConfig.configure(
enableVibration: true,
// optional β fixes a single haptic variant for ALL messages.
// omit to keep the smart MessageType-based mapping.
// hapticType: HapticFeedbackType.mediumImpact,
);
Per-call enableVibration: false still wins, so you can opt individual messages out.
Snackbars Work the Same Way
showAppSnackBar(
context,
'Network error',
messageType: MessageType.error,
enableVibration: true,
);
β οΈ Important β
showAppSnackBarvsbuildAppSnackBar: Haptic feedback only fires fromshowAppSnackBar(...)(the imperative show-and-display helper).buildAppSnackBar(...)is a pure widget builder β it returns aSnackBarwidget with no side effects, so it does not fire haptics. If you build the widget yourself and pass it toScaffoldMessenger.showSnackBar(...), vibration will not trigger. UseshowAppSnackBarwhenever you want haptics (and to honor the globalenableVibrationconfig).
π‘ Note: Haptics will not fire on the iOS Simulator or Android Emulator β test on a physical device. Users who have disabled system haptics will also see silent no-ops (expected).
βοΈ Global Configuration
Configure default behavior once in your main() function:
void main() {
EasyMessageConfig.configure(
toastDuration: Duration(seconds: 2),
snackBarDuration: Duration(seconds: 3),
toastEntryAnimationDuration: Duration(milliseconds: 400),
toastExitAnimationDuration: Duration(milliseconds: 300),
borderRadius: 12,
toastPosition: MessagePosition.bottomCenter,
toastBehavior: MessageBehavior.replace,
enablePulse: true,
enableVibration: false, // global haptic toggle (default: false)
// hapticType: HapticFeedbackType.lightImpact, // optional fixed variant
);
runApp(MyApp());
}
Animation Presets
Use built-in animation presets for consistent animations:
// Fast animations (200ms)
EasyMessageConfig.configure(
toastEntryAnimationDuration: AnimationPresets.fast.entry,
toastExitAnimationDuration: AnimationPresets.fast.exit,
);
// Normal animations (400ms) - default
EasyMessageConfig.configure(
toastEntryAnimationDuration: AnimationPresets.normal.entry,
toastExitAnimationDuration: AnimationPresets.normal.exit,
);
// Slow animations (600ms)
EasyMessageConfig.configure(
toastEntryAnimationDuration: AnimationPresets.slow.entry,
toastExitAnimationDuration: AnimationPresets.slow.exit,
);
π‘ Real-World Examples
API Error Handling with Retry
Future<void> uploadFile(File file) async {
try {
showAppToast(
'β³ Uploading ${file.name}...',
context: context,
messageType: MessageType.info,
isPersistent: true,
requestId: 'upload_${file.hashCode}',
);
await api.uploadFile(file);
await ToastManager.clearByRequestId('upload_${file.hashCode}');
showAppToast(
'β
Upload complete!',
context: context,
messageType: MessageType.success,
);
} catch (e) {
showAppToast(
'β Upload failed',
context: context,
messageType: MessageType.error,
duration: Duration(seconds: 10),
actions: [
ToastAction(
label: 'Retry',
color: Colors.green,
onPressed: () => uploadFile(file),
),
],
);
}
}
Form Validation
void validateForm(Map<String, String> data) {
if (data['email']?.isEmpty ?? true) {
showAppToast(
'Email is required',
context: context,
messageType: MessageType.error,
);
return;
}
submitForm(data);
}
π§ Best Practices
β Do's
- β Keep messages short (1-2 lines)
- β Use appropriate message types
- β Test on multiple screen sizes
- β Use context-free toasts in services
- β Use request IDs for long operations
β Don'ts
- β Spam users with multiple toasts
- β Keep toasts visible too long
- β Use complex layouts
- β Reconfigure globally too frequently
π Troubleshooting
| Issue | Solution |
|---|---|
| Toasts not showing | Set navigator key via EasyMessageConfig.setNavigatorKey() |
| Choppy animations | Try AnimationPresets.fast or run in release mode |
| Message stacking | Use MessageBehavior.replace (default) |
π Quality & Testing
- β 30/30 tests passing (100%)
- β 0 code analysis issues
- β Full null safety
- β 90%+ documentation coverage
- β Flutter 1.17.0+
- β Dart 3.11.1+
π± Responsive Design
Automatic layout adjustment for all screen sizes:
π± Mobile π± Tablet π₯οΈ Desktop
(<600px) (600-1024px) (>1024px)
All toasts adapt to screen width, height, and safe areas.
π Project Structure
flutter_easy_messages/
βββ lib/
β βββ flutter_easy_messages.dart
β βββ src/
β βββ toast_helper.dart
β βββ toast_manager.dart
β βββ message_config.dart
β βββ message_type.dart
β βββ message_position.dart
β βββ message_behavior.dart
β βββ ... (other modules)
βββ test/
β βββ flutter_easy_messages_test.dart
βββ example/
β βββ ... (demo app)
βββ pubspec.yaml
π€ Contributing
Contributions are welcome! Please:
- Report issues on GitHub
- Submit pull requests with improvements
- Help improve documentation
π License
MIT License - see LICENSE file for details
π Resources
- Example App - Complete demo with 25+ examples
- GitHub - Source code
- Pub.dev - Package registry
Made with β€οΈ for the Flutter community
β Star on GitHub if you find this helpful!
Libraries
- flutter_easy_messages
- A Flutter package for easy toast and snackbar messages.