Flutter Awesome Notification đ
A comprehensive, production-ready notification plugin for Flutter apps with Firebase Cloud Messaging (FCM) and local notifications. Handles foreground notifications with intelligent filtering and seamless navigation across all app states.
⨠Features
- â Foreground Notification Handling: Display notifications when app is active
- â Custom Filtering: Flexible callback-based filtering for app-specific logic
- â Navigation Integration: Seamless navigation across all app states (foreground/background/terminated)
- â Topic Subscriptions: Easy FCM topic management
- â Local Notifications: Immediate and scheduled local notifications
- â Highly Configurable: Builder pattern with sensible defaults
- â Minimal Code: Easy setup with very little boilerplate
- â FCM Token Management: Automatic token handling and refresh
- â Custom Logging: Integrate with your preferred logging solution
- â Type-Safe: Full type safety with comprehensive configuration
đĻ Repository
- Repository: GitHub
- Pub.dev: Package
- Issues: GitHub Issues
- Changelog: CHANGELOG.md
đ Quick Start
Installation
Add to your pubspec.yaml:
dependencies:
flutter_awesome_notification: ^1.0.0
Basic Setup
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter_awesome_notification/flutter_awesome_notification.dart';
import 'firebase_options.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Initialize the notification plugin BEFORE Firebase
await FlutterAwesomeNotification.initialize(
config: FlutterAwesomeNotificationConfig(
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
mainChannelId: 'my_app_notifications',
mainChannelName: 'My App Notifications',
onNotificationTap: (data) {
print('Notification tapped: $data');
},
onNavigate: (pageName, id, data) {
print('Navigate to: $pageName with id: $id');
},
),
);
// Initialize Firebase
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
runApp(MyApp());
}
That's it! You now have full notification support with just a few lines of code.
đ Configuration
Complete Configuration Example
await FlutterAwesomeNotification.initialize(
config: FlutterAwesomeNotificationConfig(
// REQUIRED
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
// Channel Configuration
mainChannelId: 'my_app_channel',
mainChannelName: 'My App Notifications',
mainChannelDescription: 'General notifications',
notificationIcon: '@mipmap/ic_launcher',
// Callbacks
onNotificationTap: (data) {
// Handle notification tap
print('Tapped: $data');
},
onNavigate: (pageName, id, data) {
// Custom navigation
// Example: GoRouter.of(context).push('/$pageName/$id');
},
customFilter: (messageData) async {
// Your app-specific filtering logic
// Example: Filter notifications based on type and user
final type = messageData['type'];
final excludeUserId = messageData['excludeUserId'];
final currentUserId = getCurrentUserId();
// Don't show user's own action notifications
if (type == 'action_step_completion' && excludeUserId == currentUserId) {
return false; // Don't show
}
// Show all other notifications
return true;
},
// Logging Options
// Option 1: External logger (recommended - unified logging)
// Compatible with flutter_awesome_logger and other logging solutions
externalLogger: logger, // Your logger instance with d(), i(), w(), e() methods
// Option 2: Logger callback (legacy support)
// logger: (message, {error}) {
// myLogger.log(message, error: error);
// },
// Advanced
enableLogging: true,
requestPermissionOnInit: true,
showAlertInForeground: true,
showBadgeInForeground: true,
playSoundInForeground: true,
defaultNotificationTitle: 'New Update',
defaultNotificationBody: 'You have a new update',
environment: 'production',
),
);
Minimal Configuration
For a basic setup, only Firebase options are required:
await FlutterAwesomeNotification.initialize(
config: FlutterAwesomeNotificationConfig(
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
),
);
đ¯ Usage
Access the Service
final notificationService = FlutterAwesomeNotification.instance;
Topic Subscriptions
// Subscribe to a topic
await notificationService.subscribeToTopic('announcements');
// Unsubscribe from a topic
await notificationService.unsubscribeFromTopic('announcements');
Get FCM Token
final token = await notificationService.getDeviceToken();
print('FCM Token: $token');
Show Local Notification
await notificationService.showLocalNotification(
id: 123,
title: 'Hello!',
body: 'This is a local notification',
data: {'key': 'value'},
);
Schedule Notification
await notificationService.scheduleNotification(
id: 124,
title: 'Reminder',
body: 'Don\'t forget to check this!',
scheduledDate: DateTime.now().add(Duration(hours: 2)),
data: {'reminder_type': 'task'},
);
Cancel Notifications
// Cancel specific notification
await notificationService.cancelNotification(123);
// Cancel all notifications
await notificationService.cancelAllNotifications();
Check Notification Permissions
final enabled = await notificationService.areNotificationsEnabled();
if (!enabled) {
await notificationService.requestPermissions();
}
Unified Logging with External Logger
The plugin supports external logger instances for unified logging across your app:
// 1. Create or use your existing logger instance
class MyLogger {
void d(String message) => print('đ DEBUG: $message');
void i(String message) => print('âšī¸ INFO: $message');
void w(String message) => print('â ī¸ WARNING: $message');
void e(String message, {dynamic error, StackTrace? stackTrace}) {
print('â ERROR: $message');
if (error != null) print('Error: $error');
}
}
final logger = MyLogger();
// 2. Pass it to the plugin during initialization
await FlutterAwesomeNotification.initialize(
config: FlutterAwesomeNotificationConfig(
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
enableLogging: true,
externalLogger: logger, // đ¯ Your logger instance
),
);
Benefits:
- â Unified logging across all plugins (deeplink, notification, etc.)
- â
Compatible with
flutter_awesome_loggerand other logging solutions - â Consistent log format and filtering
- â No need for custom callbacks
Supported Log Levels:
d()- Debug messages (initialization, state changes)i()- Info messages (successful operations)w()- Warning messages (non-critical issues)e()- Error messages (failures, exceptions)
đ How It Works
App State Behavior
Foreground (App Open & Visible)
- FCM Message Received â
FirebaseMessaging.onMessagestream - Custom Filtering Applied â Action steps, chat rooms, user filtering
- Local Notification Shown â Via
flutter_local_notificationsplugin - Tap Navigation â
onNavigatecallback withpageNameandid
Background (App Minimized)
- FCM Message Received â System notification (if
notificationfield present) - No Custom Filtering â Plugin doesn't run in background
- User Taps Notification â
FirebaseMessaging.onMessageOpenedApptriggers - Navigation on App Open â Same
onNavigatecallback as foreground
Terminated (App Closed)
- FCM Message Received â System notification (if
notificationfield present) - No Custom Filtering â App not running
- User Taps Notification â Cold app launch with initial message
- Navigation on Launch â
FirebaseMessaging.getInitialMessage()âonNavigate
Key Differences by App State
| Feature | Foreground | Background | Terminated |
|---|---|---|---|
| Custom Filtering | â Full | â None | â None |
| Notification Display | â Plugin | â System | â System |
| Navigation | â Immediate | â On tap | â On launch |
| Plugin Processing | â Active | â Dormant | â Dormant |
FCM Payload Requirements
For Background/Terminated delivery:
{
"notification": {
"title": "New Message",
"body": "You have a new message"
},
"data": {
"pageName": "chat-room",
"id": "room123",
"type": "message"
}
}
â ī¸ Data-only payloads won't show in background/terminated:
// â Won't show in background/terminated
{
"data": {
"pageName": "chat-room",
"id": "room123"
}
}
Custom Filtering
The plugin provides a flexible filtering system via the customFilter callback:
customFilter: (messageData) async {
// Implement your app-specific filtering logic here
// Return true to show notification, false to hide it
// Example: Filter by notification type
final type = messageData['type'];
if (type == 'spam') return false;
// Example: Filter by user
final userId = messageData['userId'];
if (userId == currentUserId) return false;
// Example: Filter by app state
final chatRoomId = messageData['chatRoomId'];
if (isUserInChatRoom(chatRoomId)) return false;
return true; // Show by default
}
đ Migration Guide
From Manual FCM Setup
If you're currently handling FCM manually, migration is straightforward:
// Before (Manual Setup)
FirebaseMessaging.onMessage.listen((message) {
// Custom filtering logic
// Manual notification display
// Navigation handling
});
FirebaseMessaging.onMessageOpenedApp.listen((message) {
// Navigation from background tap
});
FirebaseMessaging.getInitialMessage().then((message) {
// Navigation from terminated state
});
// After (Plugin)
await FlutterAwesomeNotification.initialize(
config: FlutterAwesomeNotificationConfig(
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
onNavigate: (pageName, id, data) {
// Your navigation logic here
// Works for all app states automatically
},
),
);
// That's it! Plugin handles everything else
đą Server-Side Configuration
FCM Message Format
For proper filtering, send data-only messages:
{
"data": {
"type": "action_step_completion",
"excludeUserId": "user123",
"challengeId": "challenge456",
"pageName": "challenge-details",
"id": "challenge456",
"title": "Challenge Update",
"body": "Someone completed a step!"
},
"token": "fcm_device_token"
}
đ Troubleshooting
Notifications Not Showing
Foreground Issues:
- Check if permissions are granted:
final enabled = await notificationService.areNotificationsEnabled(); - Ensure plugin is initialized before Firebase initialization
- Check if custom filters are blocking notifications
Background/Terminated Issues:
- Critical: FCM payload must include
notificationfield:{ "notification": {"title": "Title", "body": "Body"}, // REQUIRED "data": {"pageName": "route"} } - Data-only payloads won't show in background/terminated states
- Custom filtering doesn't work in background/terminated
Navigation Not Working
- Verify
onNavigatecallback is set - Ensure
pageNameis in notification data - Check navigation implementation in callback
đ Comparison
| Feature | flutter_awesome_notification | Manual Setup |
|---|---|---|
| Setup Complexity | âī¸ Simple | âī¸âī¸âī¸âī¸ Complex |
| Lines of Code | ~10 lines | ~500+ lines |
| Filtering System | â Built-in | â Manual |
| Topic Management | â Built-in | â Manual |
| Documentation | â Complete | â Variable |
| Maintenance | â Plugin updates | â Manual updates |
đ License
MIT License - see LICENSE file for details
đ¤ Contributing
We welcome contributions! Please feel free to submit issues, feature requests, or pull requests.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
đ Support & Issues
- đ Documentation: GitHub README
- đ Bug Reports: GitHub Issues
- đĄ Feature Requests: GitHub Issues
- đ§ General Questions: Use GitHub Discussions
Libraries
- flutter_awesome_notification
- Flutter Awesome Notification Plugin