π± 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! β