Home

Awesome

<div align="center">

flutter_notification_listener

Version pub points popularity likes License

Flutter plugin to listen for all incoming notifications for Android.

</div>

Features

Installtion

Open the pubspec.yaml file located inside the app folder, and add flutter_notification_listener: under dependencies.

dependencies:
  flutter_notification_listener: <latest_version>

The latest version is Version

Then you should install it,

Quick Start

1. Register the service in the manifest

The plugin uses an Android system service to track notifications. To allow this service to run on your application, the following code should be put inside the Android manifest, between the application tags.

<service android:name="im.zoe.labs.flutter_notification_listener.NotificationsHandlerService"
    android:label="Flutter Notifications Handler"
    android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService" />
    </intent-filter>
</service>

And don't forget to add the permissions to the manifest,

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

2. Init the plugin and add listen handler

We have a default static event handler which send event with a channel. So if you can listen the event in the ui logic simply.

// define the handler for ui
void onData(NotificationEvent event) {
    print(event.toString());
}

Future<void> initPlatformState() async {
    NotificationsListener.initialize();
    // register you event handler in the ui logic.
    NotificationsListener.receivePort.listen((evt) => onData(evt));
}

3. Check permission and start the service

void startListening() async {
    print("start listening");
    var hasPermission = await NotificationsListener.hasPermission;
    if (!hasPermission) {
        print("no permission, so open settings");
        NotificationsListener.openPermissionSettings();
        return;
    }

    var isR = await NotificationsListener.isRunning;

    if (!isR) {
        await NotificationsListener.startService();
    }

    setState(() => started = true);
}

Please check the ./example/lib/main.dart for more detail.

Usage

Start the service after reboot

It's every useful while you want to start listening notifications automatically after reboot.

Register a broadcast receiver in the AndroidManifest.xml,

<receiver android:name="im.zoe.labs.flutter_notification_listener.RebootBroadcastReceiver"
    android:enabled="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

Then the listening service will start automatically when the system fired the BOOT_COMPLETED intent.

And don't forget to add the permissions to the manifest,

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- this pemission is for auto start service after reboot -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

Execute task without UI thread

You should know that the function (evt) => onData(evt) would not be called if the ui thread is not running.

:warning: It's recommended that you should register your own static function callbackHandle to handle the event which make sure events consumed.

That means the callbackHandle static function is guaranteed, while the channel handle function is not. This is every useful when you should persist the events to the database.

For Flutter 3.x: Annotate the _callback function with @pragma('vm:entry-point') to prevent Flutter from stripping out this function on services.

We want to run some code in background without UI thread, like persist the notifications to database or storage.

  1. Define your own callback to handle the incoming notifications.

    @pragma('vm:entry-point')
    static void _callback(NotificationEvent evt) {
        // persist data immediately
        db.save(evt)
    
        // send data to ui thread if necessary.
        // try to send the event to ui
        print("send evt to ui: $evt");
        final SendPort send = IsolateNameServer.lookupPortByName("_listener_");
        if (send == null) print("can't find the sender");
        send?.send(evt);
    }
    
  2. Register the handler when invoke the initialize.

    Future<void> initPlatformState() async {
        // register the static to handle the events
        NotificationsListener.initialize(callbackHandle: _callback);
    }
    
  3. Listen events in the UI thread if necessary.

    // define the handler for ui
    void onData(NotificationEvent event) {
        print(event.toString());
    }
    
    Future<void> initPlatformState() async {
        // ...
        // register you event handler in the ui logic.
        NotificationsListener.receivePort.listen((evt) => onData(evt));
    }
    

Change notification of listening service

Before you start the listening service, you can offer some parameters.

await NotificationsListener.startService({
    bool foreground = true, // use false will not promote to foreground and without a notification
    String title = "Change the title",
    String description = "Change the text",
});

Tap the notification

We can tap the notification if it can be triggered in the flutter side.

For example, tap the notification automatically when the notification arrived.

// define the handler for ui
void onData(NotificationEvent event) {
    print(event.toString());
    // tap the notification automatically
    // usually remove the notification
    if (event.canTap) event.tap();
}

Tap action of the notification

The notifications from some applications will setted the actions. We can interact with the notificaions in the flutter side.

For example, make the notification as readed automatically when the notification arrived.

// define the handler for ui
void onData(NotificationEvent event) {
    print(event.toString());
    
    events.actions.forEach(act => {
        // semantic code is 2 means this is an ignore action
        if (act.semantic == 2) {
            act.tap();
        }
    })
}

Reply to conversation of the notification

Android provider a quick replying method in the notification. So we can use this to implement a reply logic in the flutter.

For example, reply to the conversation automatically when the notification arrived.

// define the handler for ui
void onData(NotificationEvent event) {
    print(event.toString());
    
    events.actions.forEach(act => {
        // semantic is 1 means reply quick
        if (act.semantic == 1) {
            Map<String, dynamic> map = {};
            act.inputs.forEach((e) {
                print("set inputs: ${e.label}<${e.resultKey}>");
                map[e.resultKey] = "Auto reply from flutter";
            });

            // send to the data
            act.postInputs(map);
        }
    })
}

API Reference

Object NotificationEvent

Fields of NotificationEvent:

Other original fields in raw which not assgin to the class:

Methods for notification:

Object Action

Fields of Action:

Action's semantic types:

SEMANTIC_ACTION_ARCHIVE = 5;
SEMANTIC_ACTION_CALL = 10;
SEMANTIC_ACTION_DELETE = 4;
SEMANTIC_ACTION_MARK_AS_READ = 2;
SEMANTIC_ACTION_MARK_AS_UNREAD = 3;
SEMANTIC_ACTION_MUTE = 6;
SEMANTIC_ACTION_NONE = 0;
SEMANTIC_ACTION_REPLY = 1;
SEMANTIC_ACTION_THUMBS_DOWN = 9;
SEMANTIC_ACTION_THUMBS_UP = 8;
SEMANTIC_ACTION_UNMUTE = 7;

For more details, please see Notification.Action Constants.

Methods of Action:

Object ActionInput

Fields of ActionInput:

Class NotificationsListener

Fields of NotificationsListener:

Static methods of NotificationsListener:

Known Issues

Support

Did you find this plugin useful? Please consider to make a donation to help improve it!

Contributing

Contributions are always welcome!