| .claude | ||
| agent-ops | ||
| agent-task | ||
| android | ||
| example | ||
| ios | ||
| lib | ||
| macos | ||
| test | ||
| .aiexclude | ||
| .clineignore | ||
| .clinerules | ||
| .cursorignore | ||
| .cursorrules | ||
| .geminiignore | ||
| .gitignore | ||
| .metadata | ||
| AGENTS.md | ||
| analysis_options.yaml | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| GEMINI.md | ||
| LICENSE | ||
| opencode.json | ||
| pubspec.yaml | ||
| README.md | ||
mattermost_push_plugin
An Android-first Flutter plugin for receiving Mattermost push notifications, displaying system notifications, handling tap events, processing inline replies, delivering ACKs, and securely storing device tokens, authentication tokens, and signing keys.
Support Matrix
| Platform | Support Status | Native Features Implemented |
|---|---|---|
| Android | Full (Production Ready) | Kotlin, Native FCM Service, Room DB, JJWT Verification, OkHttp ACK Client, Notification Builder, Inline Reply Receiver, Dismiss Receiver |
| iOS | No-op Scaffold (Stub) | Future roadmap milestone |
| macOS | No-op Scaffold (Stub) | Future roadmap milestone |
Core Responsibilities
This plugin isolates the Mattermost push notification domain logic from the host application.
graph TD
A[Mattermost Server] -->|Secure JWT Push| B(Plugin Native FCM Service)
B -->|1. Signature Verification JJWT| C{Valid?}
C -->|Yes| D[2. Store & Build Notification]
C -->|No| E[Drop Push]
D -->|3. OkHttp ACK API| A
D -->|4. Display System Notification| F(Android Status Bar)
F -->|Tap Notification| G[5. Method Channel Opened Event]
F -->|Inline Reply| H[6. Broadcast Reply Receiver]
H -->|Deliver via OkHttp| A
G -->|Flutter Stream| I(Host App Navigation)
Public Dart API Reference
Get the singleton instance via MattermostPushPlugin.instance.
1. Initialization
Initialize the plugin channel subscriptions, listen to token refreshes, and request notification permissions:
import 'package:mattermost_push_plugin/mattermost_push_plugin.dart';
await MattermostPushPlugin.instance.initialize();
2. Streams & Callbacks
Set up listeners and navigation callbacks inside your app initialization flow:
// 1. Listen to raw native notification events (Message, Clear, Session, etc.)
MattermostPushPlugin.instance.onNotification.listen((data) {
print("Raw push received: $data");
});
// 2. Listen to user interaction (notification tap) events
MattermostPushPlugin.instance.onNotificationOpened.listen((event) {
print("Notification clicked! Channel: ${event.channelId}, Root: ${event.rootId}");
});
// 3. Register navigation handlers (automatically triggered by onNotificationOpened)
MattermostPushPlugin.instance.onNavigateToChannel = (String serverUrl, String channelId) {
// Navigate the app to the specific channel
};
MattermostPushPlugin.instance.onNavigateToThread = (String serverUrl, String rootId) {
// Navigate the app to the specific CRT thread
};
// 4. Triggered when the device token is registered/refreshed
MattermostPushPlugin.instance.onDeviceTokenReady = (String deviceToken) {
// e.g. "android_rn-v2:YOUR_FCM_TOKEN"
// Send this token to the Mattermost server for token registration
};
3. Credential & Signing Key Storing
Securely write server-specific secrets to native persistent storage. These are required by the native service to decrypt and verify the authenticity of push messages.
// Save authenticating token for HTTP ACK requests
await MattermostPushPlugin.instance.setAuthToken(
"https://mattermost.example.com",
"YOUR_USER_AUTH_TOKEN",
identifier: "user_session_id", // optional
);
// Save public signing key for verification
await MattermostPushPlugin.instance.setSigningKey(
"https://mattermost.example.com",
"YOUR_SERVER_PUBLIC_SIGNING_KEY",
);
// Clear token on logout
await MattermostPushPlugin.instance.clearAuthToken("https://mattermost.example.com");
// Fetch the formatted device token
String? token = await MattermostPushPlugin.instance.getDeviceToken();
Host App Integration & Responsibilities
The host application (apps/mobile) acts as the consumer of the plugin and is responsible for:
- Firebase Configuration: Include the
google-services.jsoninandroid/app/and initialize Firebase in the Dart main block. - Authentication Flow: Call
setAuthTokenandsetSigningKeyon successful Mattermost login, andclearAuthTokenon logout. - App Navigation: Implement
onNavigateToChannelandonNavigateToThreadcallbacks to route users inside the Flutter UI. - FCM Token Registration: Send the formatted token received in
onDeviceTokenReadyto the Mattermost server.
Firebase Messaging Manifest Collision Guard
This plugin implements a custom Android FCM service (MattermostFirebaseMessagingService) that consumes incoming FCM payloads. To prevent manifest merge collisions with the standard Flutter firebase_messaging library, the plugin's AndroidManifest.xml disables the generic Flutter Firebase service via tools:node="remove".
Ensure that the host app does not redeclare duplicate FCM services unless explicitly configured to do so.
Test Harness Strategy
This repository keeps the plugin and its independent consumer test app together:
mattermost-push-plugin/
lib/ # Public Dart API and Flutter-facing behavior
android/ # Android native plugin implementation
test/ # Dart unit tests for the plugin contract
example/ # Thin Flutter app used as the integration test harness
test/ # Widget tests for the harness app
integration_test/ # Device/emulator integration tests
The example/ app is the official place to validate plugin integration without coupling this package to any consuming product app. Consumer apps should only test their own wiring, such as calling initialization, storing credentials, and connecting navigation callbacks.
Recommended verification layers:
flutter testfrom the repository root for Dart API behavior, event parsing, callback routing, and token formatting.flutter testfromexample/for the test harness app.- Android unit tests from
example/androidfor native payload, storage, signing, and ACK behavior. flutter test integration_testfromexample/on a device or emulator for plugin registration, method/event channels, notification-open routing, and manifest integration.- Manual smoke tests for true Firebase FCM delivery and real Mattermost server ACK flows, because those depend on external infrastructure.
Recommended local commands:
flutter analyze
flutter test
(cd example && flutter analyze && flutter test)
(cd example/android && ./gradlew testDebugUnitTest)
Android Gradle tests require a configured Android SDK via ANDROID_HOME or example/android/local.properties.
Verification & Manual Smoke Testing Checklist
Since full end-to-end FCM flows cannot be automated in headless test runners, verify functionality manually using the following checklist:
- FCM Payload Receipt: Verify that sending an FCM notification payload targeting the host application successfully wakes up
MattermostFirebaseMessagingService. - Signature Verification: Ensure that a valid push payload signed by the Mattermost server's private key compiles and passes the native JJWT verification using the stored signing key.
- Notification Display: Verify the system notification builds with the correct title, message, avatar (if supplied), and CRT threading hierarchy.
- ACK Delivery: Verify the plugin sends a HTTP
POSTreceipt delivery ACK back to the Mattermost server and logs a200 OKor201 Createdresponse. - Notification Tap & Route: Tap the system notification and verify the host app launches/resumes, triggers the
onNotificationOpenedstream, and executes the designated navigation callback. - Inline Reply Action: Click "Reply" directly on the system notification. Type a reply, press send, and verify the native broadcast receiver intercepts the text, writes it to the local Room database, and sends it via HTTP to the Mattermost API.
- Notification Dismiss (Clear): Swipe away or dismiss a notification and verify the
NotificationDismissServiceis triggered, clearing the corresponding notification records natively. - Token Storage & Generation: Ensure the generated token has the correct
android_rn-v2:prefix and is saved securely in the device's native database/shared preferences.