A powerful Flutter plugin for listening to all incoming notifications on Android
Features · Installation · Quick Start · API Reference · Contributing
|
🔔 Real-time Listening Capture all system notifications as they arrive, from any app installed on the device. |
🎯 Simple API Clean and intuitive API design. Access notification fields with ease. |
|
⚡ Background Execution Run Dart code in the background. Auto-start service after device reboot. |
🎮 Interactive Tap notifications, trigger actions, and even auto-reply to messages directly from Flutter. |
| Android Version | API Level | Status |
|---|---|---|
| Android 14 | API 34 | ✅ Fully Supported |
| Android 13 | API 33 | ✅ Fully Supported |
| Android 12 | API 31-32 | ✅ Fully Supported |
| Android 11 and below | API ≤ 30 | ✅ Fully Supported |
Add the dependency to your pubspec.yaml:
dependencies:
flutter_notification_listener: ^1.4.0Then run:
flutter pub getAdd the notification listener service inside the <application> tag:
<service
android:name="im.zoe.labs.flutter_notification_listener.NotificationsHandlerService"
android:label="Flutter Notifications Handler"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true"
android:foregroundServiceType="specialUse">
<property
android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="notification_listener"/>
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>Add required permissions:
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE"/>Note: For Android 13+, request the
POST_NOTIFICATIONSpermission at runtime before starting the foreground service.
import 'package:flutter_notification_listener/flutter_notification_listener.dart';
void onData(NotificationEvent event) {
print('${event.packageName}: ${event.title} - ${event.text}');
}
Future<void> initPlatformState() async {
NotificationsListener.initialize();
NotificationsListener.receivePort?.listen((evt) => onData(evt));
}Future<void> startListening() async {
final hasPermission = await NotificationsListener.hasPermission ?? false;
if (!hasPermission) {
NotificationsListener.openPermissionSettings();
return;
}
final isRunning = await NotificationsListener.isRunning ?? false;
if (!isRunning) {
await NotificationsListener.startService(
foreground: true,
title: "Listening for notifications",
);
}
}📖 See the example app for a complete implementation.
Register a broadcast receiver to automatically restart the service after device reboot:
<receiver
android:name="im.zoe.labs.flutter_notification_listener.RebootBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>Add the boot permission:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>For reliable background execution, use a static callback function:
@pragma('vm:entry-point')
static void _callback(NotificationEvent evt) {
// Persist data immediately
db.save(evt);
// Forward to UI thread if needed
final send = IsolateNameServer.lookupPortByName("_listener_");
send?.send(evt);
}
Future<void> initPlatformState() async {
NotificationsListener.initialize(callbackHandle: _callback);
}Important: The
@pragma('vm:entry-point')annotation is required for Flutter 3.x to prevent the function from being stripped in release builds.
Tap a notification:
void onData(NotificationEvent event) {
if (event.canTap) {
event.tap();
}
}Auto-reply to messages:
void onData(NotificationEvent event) {
for (final action in event.actions ?? []) {
if (action.semantic == 1) { // SEMANTIC_ACTION_REPLY
final inputs = <String, dynamic>{};
for (final input in action.inputs ?? []) {
inputs[input.resultKey ?? ''] = "Auto-reply from Flutter";
}
action.postInputs(inputs);
}
}
}await NotificationsListener.startService(
foreground: true,
title: "My App",
description: "Listening for notifications...",
);| Property | Type | Description |
|---|---|---|
uniqueId |
String |
Unique identifier generated from key |
packageName |
String |
Source application package name |
title |
String? |
Notification title |
text |
String? |
Notification body text |
timestamp |
int |
Post time (milliseconds since epoch) |
channelId |
String? |
Notification channel ID (API 26+) |
largeIcon |
Uint8List? |
Large icon as bytes |
canTap |
bool |
Whether notification has a tap action |
actions |
List<Action>? |
Available notification actions |
raw |
Map<String, dynamic> |
Raw notification data |
Methods:
| Method | Returns | Description |
|---|---|---|
tap() |
Future<bool> |
Trigger the notification's tap action |
getFull() |
Future<dynamic> |
Get complete notification data |
| Property | Type | Description |
|---|---|---|
id |
int |
Action index |
title |
String? |
Action button text |
semantic |
int |
Semantic type (see below) |
inputs |
List<ActionInput>? |
Input fields for reply actions |
Semantic Types:
| Constant | Value | Description |
|---|---|---|
SEMANTIC_ACTION_NONE |
0 | No semantic |
SEMANTIC_ACTION_REPLY |
1 | Quick reply |
SEMANTIC_ACTION_MARK_AS_READ |
2 | Mark as read |
SEMANTIC_ACTION_MARK_AS_UNREAD |
3 | Mark as unread |
SEMANTIC_ACTION_DELETE |
4 | Delete |
SEMANTIC_ACTION_ARCHIVE |
5 | Archive |
SEMANTIC_ACTION_MUTE |
6 | Mute |
SEMANTIC_ACTION_UNMUTE |
7 | Unmute |
| Method | Description |
|---|---|
initialize({callbackHandle}) |
Initialize the plugin |
hasPermission |
Check notification access permission |
isRunning |
Check if service is running |
openPermissionSettings() |
Open system permission settings |
startService({...}) |
Start the listener service |
stopService() |
Stop the listener service |
promoteToForeground({...}) |
Promote service to foreground |
demoteToBackground() |
Demote service to background |
- Service may fail to start after reboot if not running in foreground mode.
If you find this plugin useful, please consider:
- ⭐ Starring the repository
- 🐛 Reporting bugs
- 💡 Suggesting new features
- 🤝 Contributing code
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your 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
Made with ❤️ by Zoe