easy_in_app_notify 2.3.2
easy_in_app_notify: ^2.3.2 copied to clipboard
Beautiful Flutter in-app notifications with smooth animations, auto-dismiss, progress bars, and customizable styling.
π± Easy In-App Notify #
Beautiful, customizable in-app notifications for Flutter applications
π Overview #
Easy In-App Notify is a powerful Flutter package that provides beautiful overlay notifications with smooth animations, customizable theming, and universal widget support. Perfect for displaying non-blocking notifications when your app is in the foreground.
Latest Version: 2.3.2 - Now with centralized auto-dismiss system, universal dismiss support, and enhanced architecture!
πΈ Screenshots #
Standard Notification | Custom Widget | v2.3.2 Features |
---|---|---|
![]() |
![]() |
![]() |
Built-in notification with progress bar | Custom Card widget with icons | v2.3.2 features demonstration |
β¨ Features #
π New in v2.3.2 #
- πΌοΈ Fixed Image Display - Resolved screenshot visibility issues in documentation
- π URL Management - Updated GitHub raw URLs for proper cross-branch compatibility
- π Enhanced Documentation - Improved visual consistency and accessibility
π Previous Updates (v2.3.1) #
- π§ Fixed onTap Callback - Critical bug fix for tap event handling
- π± Enhanced Documentation - New Samsung Galaxy A14 screenshot
- π SEO Optimized - Package description optimized for better discoverability
- πΌοΈ Visual Improvements - Professional three-column screenshot layout
π Core Features (v2.3.0+) #
- π― Centralized Auto-Dismiss - Universal timer management for all notification types
- π§ Universal Dismiss Support -
dismiss()
works with any widget (Card, Container, custom widgets) - β Duration Validation - Ensures reliable auto-dismiss behavior (duration > 0)
- π Enhanced Documentation - Comprehensive code documentation with examples
- ποΈ Refactored Architecture - Improved maintainability and code organization
π Core Capabilities #
- π± Overlay Notifications - Non-blocking notifications over your app content
- π¨ Custom Widget Support - Display any Flutter widget as a notification
- β±οΈ Configurable Auto-Dismiss - Set custom durations with visual progress indicators
- π Swipe to Dismiss - Intuitive gesture-based dismissal
- π Smooth Animations - Beautiful slide-in/out transitions with easing curves
- π«οΈ Animated Blur Background - Modern iOS-style backdrop effects
- π Platform-Optimized Sounds - Smart sound selection per platform
- π Callback Support - Handle tap events and dismissal callbacks
- π Universal Compatibility - Works with Material Design, Cupertino, and custom themes
π¦ Installation #
Using Flutter CLI #
flutter pub add easy_in_app_notify
Manual Installation #
Add to your pubspec.yaml
:
dependencies:
easy_in_app_notify: ^2.3.0
Then run:
flutter pub get
π Quick Start #
Basic Usage #
import 'package:easy_in_app_notify/easy_in_app_notify.dart';
// Show a simple notification
EasyInAppNotify.show(
context,
view: EasyInAppView(
content: EasyInAppNotifyContent(
title: "Success!",
message: "Your action was completed successfully.",
),
),
option: EasyInAppNotifyOption(
duration: 3, // Auto-dismiss after 3 seconds
),
);
Custom Widget Notification (v2.3.0) #
// Display any custom widget as a notification
EasyInAppNotify.show(
context,
view: Card(
margin: EdgeInsets.all(16),
child: Padding(
padding: EdgeInsets.all(20),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.check_circle, color: Colors.green, size: 32),
SizedBox(height: 8),
Text("Custom Notification", style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
Text("This is a custom Card widget!"),
],
),
),
),
option: EasyInAppNotifyOption(
duration: 4,
swipeToDismiss: true,
),
);
Manual Dismissal (Universal Support) #
// Show notification
EasyInAppNotify.show(context, view: myWidget, option: myOptions);
// Dismiss programmatically (works with ANY widget type)
EasyInAppNotify.dismiss();
ποΈ Configuration #
EasyInAppNotifyContent #
Configure the notification content for standard notifications:
EasyInAppNotifyContent(
title: "Notification Title", // Required: Main heading
message: "Detailed message content", // Required: Description text
icon: Icons.notifications, // Optional: Leading icon
trailingText: "2m ago", // Optional: Trailing text
)
Property | Type | Description | Default |
---|---|---|---|
title |
String |
Main notification title | Required |
message |
String |
Notification message content | Required |
icon |
IconData? |
Optional leading icon | CupertinoIcons.bell |
trailingText |
String? |
Optional trailing text | null |
trailing |
Widget? |
Custom trailing widget | null |
EasyInAppNotifyOption #
Control notification timing and auto-dismiss behavior:
EasyInAppNotifyOption(
duration: 5, // Auto-dismiss duration (must be > 0)
)
Property | Type | Description | Default |
---|---|---|---|
duration |
int |
Auto-dismiss duration in seconds (must be > 0) | 5 |
β οΈ Important: In v2.3.0,
duration
must be greater than 0. Useduration: 1
or higher.π Note:
showProgressBar
andswipeToDismiss
have been moved toEasyInAppNotifyTheme
for better organization.
EasyInAppNotifyTheme #
Customize visual appearance and styling:
EasyInAppNotifyTheme(
color: Colors.blue, // Primary accent color
margin: 16.0, // Margin from screen edges
radius: 12.0, // Border radius
elevation: 8.0, // Shadow elevation
blurBackground: true, // Enable blur effect
showProgressBar: true, // Show countdown progress bar
swipeToDismiss: true, // Enable swipe gestures
)
Property | Type | Description | Default |
---|---|---|---|
color |
Color? |
Primary accent color | App's primary color |
margin |
double |
Margin from screen edges | 5.0 |
padding |
double |
Internal padding | 10.0 |
radius |
double |
Border radius | 10.0 |
elevation |
double |
Shadow elevation | 5.0 |
iconSize |
double |
Icon size | 20.0 |
blurBackground |
bool |
Enable blur effect | true |
blurColor |
Color |
Blur overlay color | Colors.black |
showProgressBar |
bool |
Display countdown progress bar | true |
swipeToDismiss |
bool |
Enable swipe-to-dismiss gestures | true |
π¨ Advanced Examples #
Interactive Notification with Callbacks #
EasyInAppNotify.show(
context,
view: EasyInAppView(
content: EasyInAppNotifyContent(
title: "New Message",
message: "You have received a new message",
icon: Icons.message,
),
theme: EasyInAppNotifyTheme(
showProgressBar: true, // Now in theme
swipeToDismiss: true, // Now in theme
),
onTap: () {
// Handle notification tap
Navigator.pushNamed(context, '/messages');
},
),
option: EasyInAppNotifyOption(
duration: 6, // Only timing in options
),
onDismissed: () {
// Handle dismissal
print('Notification was dismissed');
},
);
Custom Themed Notification #
EasyInAppNotify.show(
context,
view: EasyInAppView(
content: EasyInAppNotifyContent(
title: "Premium Feature",
message: "Unlock advanced features with Premium",
icon: Icons.star,
),
theme: EasyInAppNotifyTheme(
color: Colors.amber,
margin: 20.0,
radius: 16.0,
elevation: 12.0,
blurBackground: true,
blurColor: Colors.orange,
showProgressBar: false, // Visual elements in theme
swipeToDismiss: true, // Gesture handling in theme
),
),
option: EasyInAppNotifyOption(
duration: 4, // Only timing configuration
),
);
Complex Custom Widget #
EasyInAppNotify.show(
context,
view: Container(
margin: EdgeInsets.all(16),
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.purple, Colors.pink],
),
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Row(
children: [
Icon(Icons.celebration, color: Colors.white, size: 32),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
"Achievement Unlocked!",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
),
),
Text(
"You've completed 100 tasks",
style: TextStyle(color: Colors.white70),
),
],
),
),
ElevatedButton(
onPressed: () => EasyInAppNotify.dismiss(),
child: Text("Claim"),
),
],
),
),
option: EasyInAppNotifyOption(
duration: 8,
swipeToDismiss: true,
),
);
ποΈ Using Without Direct Context Access #
When you need to show notifications from classes that don't have access to BuildContext (like service classes, static methods, or background tasks), here are several proven patterns:
π Table of Contents #
- Pass Context as Parameter
- Callback Pattern
- Navigator Key Pattern
- Service Locator Pattern
- Provider/State Management
1. Pass Context as Parameter Pattern #
The simplest approach - pass context to your service methods:
class NotificationService {
static void showSuccess(BuildContext context, String message) {
EasyInAppNotify.show(
context,
view: EasyInAppViewView(
content: EasyInAppNotifyContent(
title: "Success",
message: message,
icon: Icons.check_circle,
),
),
option: EasyInAppNotifyOption(duration: 3),
);
}
static void showError(BuildContext context, String message) {
EasyInAppNotify.show(
context,
view: Card(
margin: EdgeInsets.all(16),
color: Colors.red.shade50,
child: Padding(
padding: EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.error, color: Colors.red),
SizedBox(width: 12),
Expanded(
child: Text(message, style: TextStyle(color: Colors.red.shade800)),
),
],
),
),
),
option: EasyInAppNotifyOption(duration: 4),
);
}
}
// Usage in widgets
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
// Call service method with context
NotificationService.showSuccess(context, "Operation completed!");
},
child: Text("Show Success"),
);
}
}
2. Callback Pattern #
Decouple services from UI by using callbacks:
class ApiService {
static VoidCallback? _onNotificationRequest;
// Service registers callback
static void setNotificationCallback(VoidCallback callback) {
_onNotificationRequest = callback;
}
static Future<void> fetchData() async {
try {
// Simulate API call
await Future.delayed(Duration(seconds: 2));
// Trigger notification through callback
_onNotificationRequest?.call();
} catch (e) {
// Handle error
}
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Builder(
builder: (context) {
// Register notification callback
ApiService.setNotificationCallback(() {
EasyInAppNotify.show(
context,
view: EasyInAppViewView(
content: EasyInAppNotifyContent(
title: "Data Loaded",
message: "Successfully fetched latest data",
),
),
option: EasyInAppNotifyOption(duration: 3),
);
});
return MyHomePage();
},
),
);
}
}
3. Navigator Key Pattern #
Use a global navigator key for app-wide access:
// globals.dart
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class NotificationManager {
static void showNotification({
required String title,
required String message,
IconData? icon,
int duration = 5,
}) {
final context = navigatorKey.currentContext;
if (context != null) {
EasyInAppNotify.show(
context,
view: EasyInAppViewView(
content: EasyInAppNotifyContent(
title: title,
message: message,
icon: icon ?? Icons.info,
),
),
option: EasyInAppNotifyOption(duration: duration),
);
}
}
static void showCustomNotification(Widget customWidget) {
final context = navigatorKey.currentContext;
if (context != null) {
EasyInAppNotify.show(
context,
view: customWidget,
option: EasyInAppNotifyOption(duration: 4),
);
}
}
}
// main.dart
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey, // Register global key
home: MyHomePage(),
);
}
}
// Usage anywhere in your app
class BackgroundService {
static void processData() {
// Simulate background processing
Timer(Duration(seconds: 3), () {
// Show notification from background service
NotificationManager.showNotification(
title: "Processing Complete",
message: "Your data has been processed successfully",
icon: Icons.done_all,
);
});
}
}
4. Service Locator Pattern #
Use dependency injection for clean architecture:
// notification_service.dart
abstract class INotificationService {
void showSuccess(String message);
void showError(String message);
void showCustom(Widget widget);
}
class NotificationService implements INotificationService {
final BuildContext _context;
NotificationService(this._context);
@override
void showSuccess(String message) {
EasyInAppNotify.show(
_context,
view: EasyInAppViewView(
content: EasyInAppNotifyContent(
title: "Success",
message: message,
icon: Icons.check_circle,
),
),
option: EasyInAppNotifyOption(duration: 3),
);
}
@override
void showError(String message) {
EasyInAppNotify.show(
_context,
view: Card(
margin: EdgeInsets.all(16),
color: Colors.red.shade50,
child: ListTile(
leading: Icon(Icons.error, color: Colors.red),
title: Text("Error"),
subtitle: Text(message),
),
),
option: EasyInAppNotifyOption(duration: 5),
);
}
@override
void showCustom(Widget widget) {
EasyInAppNotify.show(
_context,
view: widget,
option: EasyInAppNotifyOption(duration: 4),
);
}
}
// service_locator.dart
class ServiceLocator {
static final Map<Type, dynamic> _services = {};
static void register<T>(T service) {
_services[T] = service;
}
static T get<T>() {
return _services[T] as T;
}
}
// Usage
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Register service with current context
ServiceLocator.register<INotificationService>(
NotificationService(context),
);
return Scaffold(
body: BusinessLogicWidget(),
);
}
}
class BusinessLogicWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () {
// Use service without direct context access
final notificationService = ServiceLocator.get<INotificationService>();
notificationService.showSuccess("Operation completed!");
},
child: Text("Process Data"),
);
}
}
5. Provider/State Management Pattern #
Integrate with popular state management solutions:
Using Provider
// notification_provider.dart
class NotificationProvider extends ChangeNotifier {
BuildContext? _context;
void setContext(BuildContext context) {
_context = context;
}
void showNotification({
required String title,
required String message,
Widget? customView,
}) {
if (_context != null) {
EasyInAppNotify.show(
_context!,
view: customView ?? EasyInAppViewView(
content: EasyInAppNotifyContent(
title: title,
message: message,
),
),
option: EasyInAppNotifyOption(duration: 4),
);
}
}
}
// main.dart
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => NotificationProvider(),
child: MyApp(),
),
);
}
// Usage in widgets
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final notificationProvider = Provider.of<NotificationProvider>(context);
// Set context on first build
WidgetsBinding.instance.addPostFrameCallback((_) {
notificationProvider.setContext(context);
});
return ElevatedButton(
onPressed: () {
notificationProvider.showNotification(
title: "Provider Notification",
message: "Sent from Provider pattern!",
);
},
child: Text("Show Notification"),
);
}
}
Using Riverpod
// providers.dart
final notificationProvider = StateNotifierProvider<NotificationNotifier, void>(
(ref) => NotificationNotifier(),
);
class NotificationNotifier extends StateNotifier<void> {
NotificationNotifier() : super(null);
BuildContext? _context;
void setContext(BuildContext context) {
_context = context;
}
void showSuccess(String message) {
if (_context != null) {
EasyInAppNotify.show(
_context!,
view: EasyInAppViewView(
content: EasyInAppNotifyContent(
title: "Success",
message: message,
icon: Icons.check,
),
),
option: EasyInAppNotifyOption(duration: 3),
);
}
}
}
π― Best Practices for Context-less Usage #
-
Choose the Right Pattern:
- Simple apps: Pass context as parameter
- Service-oriented: Callback or Navigator Key pattern
- Complex apps: Service Locator or State Management
-
Context Lifecycle Management:
// Always check context validity if (context != null && context.mounted) { EasyInAppNotify.show(context, view: widget); }
-
Error Handling:
try { EasyInAppNotify.show(context, view: widget); } catch (e) { // Fallback to console logging or other notification method print('Failed to show notification: $e'); }
-
Memory Management:
// Clear references when appropriate class MyService { BuildContext? _context; void dispose() { _context = null; } }
π§ API Reference #
Static Methods #
show()
static void show(
BuildContext context, {
required Widget view,
EasyInAppNotifyOption option = const EasyInAppNotifyOption(),
VoidCallback? onDismissed,
})
Displays a notification with the specified widget and options.
dismiss()
static void dismiss()
Programmatically dismisses the current notification with animations (v2.3.0+).
hide()
static void hide()
Immediately hides the current notification without animations.
isShowing
static bool get isShowing
Returns true
if a notification is currently displayed.
π¨ Migration Guide #
Upgrading to v2.3.0 #
Breaking Changes
Duration Validation:
// β No longer allowed (will throw assertion error)
EasyInAppNotifyOption(duration: 0)
// β
Use minimum duration of 1 second
EasyInAppNotifyOption(duration: 1)
New Features
Universal Dismiss Support:
// β
Now works with ANY widget type
EasyInAppNotify.dismiss(); // Works with Cards, Containers, custom widgets, etc.
Enhanced Documentation:
- All classes now have comprehensive documentation
- Better examples and usage patterns
- Improved error messages and debugging information
π οΈ Best Practices #
1. Use Appropriate Durations #
// Short messages
EasyInAppNotifyOption(duration: 3)
// Longer content that requires reading time
EasyInAppNotifyOption(duration: 6)
// Interactive notifications
EasyInAppNotifyOption(duration: 8)
2. Handle Edge Cases #
// Check if notification is already showing
if (!EasyInAppNotify.isShowing) {
EasyInAppNotify.show(context, view: myWidget);
}
3. Provide User Feedback #
EasyInAppNotify.show(
context,
view: myWidget,
onDismissed: () {
// Log analytics, update state, etc.
analytics.logNotificationDismissed();
},
);
4. Use Consistent Theming #
// Create app-wide theme
final appNotifyTheme = EasyInAppNotifyTheme(
color: Theme.of(context).primaryColor,
radius: 12.0,
elevation: 6.0,
);
// Use consistently throughout app
EasyInAppNotify.show(
context,
view: EasyInAppViewView(
content: content,
theme: appNotifyTheme,
),
);
π Troubleshooting #
Common Issues #
"Overlay not found" Error:
- Ensure you're calling
show()
from a context within MaterialApp/WidgetsApp - The context must have access to an Overlay
Notifications not dismissing:
- Check that
duration > 0
in EasyInAppNotifyOption - Verify you're using
dismiss()
method correctly
Styling issues:
- Ensure custom widgets respect Material Design guidelines
- Test themes on different devices and orientations
π Requirements #
- Flutter: >= 1.17.0
- Dart: >= 3.9.0
- Platforms: iOS, Android, Web, Desktop
π€ Contributing #
We welcome contributions! Please:
- Fork the repository
- Create a feature branch
- Make your changes with tests
- Submit a pull request
Development Setup #
git clone https://github.com/mohamedmaher-dev/easy_in_app_notify.git
cd easy_in_app_notify
flutter pub get
flutter test
π License #
This project is licensed under the MIT License - see the LICENSE file for details.
π Links #
- Package: pub.flutter-io.cn/packages/easy_in_app_notify
- Repository: GitHub
- Issues: Bug Reports
- Documentation: API Reference
π Acknowledgments #
Built with β€οΈ for the Flutter community.
Special thanks to all contributors and users who help improve this package!
β If you find this package helpful, please give it a star on GitHub! β