Flutter Awesome Notification π
A comprehensive, production-ready notification plugin for Flutter apps with Firebase Cloud Messaging (FCM) and local notifications. Handles foreground, background and terminated notifications and navigation on notification tap.
β¨ Features
- β Foreground Notification Handling: Intelligent foreground notification management
- β Intelligent Filtering: Action step, chat room, and custom notification filtering
- β Navigation Integration: Custom callbacks for navigation handling
- β 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 TypeScript-style type safety
π Quick Start
Installation
Add to your pubspec.yaml:
dependencies:
flutter_awesome_notification: ^0.0.2
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: FlutterFlutterAwesomeNotificationConfig(
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: FlutterFlutterAwesomeNotificationConfig(
// 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');
},
getCurrentUserId: () {
// Return current user ID for filtering
return userCubit.getUserModel()?.id;
},
customFilter: (messageData) async {
// Custom filtering logic
// Return true to show, false to hide
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);
// },
// Filtering Options
enableActionStepFiltering: true,
enableChatRoomFiltering: true,
isActiveChatRoom: (chatRoomId) {
// Check if user is currently in this chat room
return appCubit.isActiveChatRoom(chatRoomId);
},
chatPageRoute: 'chat-page',
// Notification Types
notificationTypeToPage: {
'action_step': 'challenge-details',
'chat_message': 'chat-room',
'event_reminder': 'event-details',
},
// 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: FlutterFlutterAwesomeNotificationConfig(
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"
}
}
Filtering System
The plugin provides multiple layers of filtering:
- Action Step Filtering: Prevents users from seeing their own action notifications
- Chat Room Filtering: Hides notifications when user is in the chat room
- Custom Filtering: Your own logic via callback
- Type Filtering: Filter by notification type
π§ Migration from Existing Service
If you're using the notification_service/ from challenge_app, here's how to migrate:
Before (Old Code)
// In main.dart
NotificationService.registerBackgroundMessageHandler();
await Firebase.initializeApp();
// In your app
await getIt<NotificationService>().initialize();
After (Plugin)
// In main.dart
await FlutterAwesomeNotification.initialize(
config: FlutterAwesomeNotificationConfig(
firebaseOptions: DefaultFirebaseOptions.currentPlatform,
onNotificationTap: (data) => AutoNavigation.handleNotificationTap(data),
getCurrentUserId: () => getIt<UserCubit>().getUserModel()?.id,
isActiveChatRoom: (id) => getIt<MyAppCubit>().isActiveChatRoom(id),
chatPageRoute: RouteNames.chatPage,
logger: (msg, {error}) => logger.d(msg, error: error),
),
);
await Firebase.initializeApp();
// That's it! No need for separate initialization
Example: my_bottom_nav_bar.dart
Future<void> _initializeServices() async {
logger.i('MyBottomNavBar: Initializing services');
try {
final notificationService = FlutterAwesomeNotification.instance;
// Optional: Subscribe to topics
// await notificationService.subscribeToTopic('challenges');
logger.i('MyBottomNavBar: Services initialized');
} catch (e) {
logger.e('MyBottomNavBar: Error initializing services', error: e);
}
}
π± 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
Contributions are welcome! Please open an issue or submit a PR.
π Support
For issues, questions, or feature requests, please open an issue on GitHub.
Libraries
- flutter_awesome_notification
- Flutter Awesome Notification Plugin