flutter_notify_sphere 1.0.1
flutter_notify_sphere: ^1.0.1 copied to clipboard
Flutter SDK for NotifySphere (notifysphere.net) to monitor push notification delivery confirmations, click tracking, and user subscriptions on Android and iOS.
Flutter NotifySphere (flutter_notify_sphere) #
Official Flutter SDK for NotifySphere. Easily monitor and track push notification delivery confirmation, click rates, and subscriber metadata across Android and iOS in real-time.
Features #
- 🚀 Accurate Delivery Tracking: Confirms delivery when notifications arrive in the foreground, background isolate, or terminated state (via iOS Notification Service Extension).
- 🖱️ Click & Open Analytics: Tracks user clicks across foreground heads-up notifications, background resumes, and cold starts from terminated state.
- 📱 Device & Subscriber Synchronization: Automatically collects and updates FCM/APNs tokens, device model, OS version, app version, network connectivity type, timezone, and optional geolocation.
- 🖼️ Rich Notifications: Native support for expandable images (Android
BigPictureStyleand iOSUNNotificationAttachment). - 🔔 Custom Notification Sounds: Custom notification channels and sounds supported on both platforms.
- 🔄 Offline Pending Queue: Delivery and click events received while offline or before initial registration are queued securely and flushed automatically once registration completes.
Architecture Flow #
flowchart TD
subgraph Client ["Client Application (Flutter & Native iOS)"]
Init["FlutterNotifySphere.initialize()"]
Reg["FlutterNotifySphere.registerUser()"]
FG["Foreground: FirebaseMessaging.onMessage"]
BG["Background: notifyBackgroundHandler"]
Cold["Cold Start: getInitialMessage()"]
Ext["iOS Service Extension: NotifySphereExtension.didReceive()"]
end
subgraph Backend ["NotifySphere Panel (notifysphere.net)"]
API_Reg["POST /client/{appId}/users"]
API_Deliv["POST /client/{appId}/delivery/confirm"]
API_Click["POST /client/{appId}/onpress/handle"]
end
Reg -->|Device Metadata + FCM Token| API_Reg
FG -->|Delivery Confirmation| API_Deliv
BG -->|Background Delivery Confirmation| API_Deliv
Ext -->|Silent Delivery Confirmation| API_Deliv
Cold -->|Click Event Tracking| API_Click
1. NotifySphere Panel Setup (notifysphere.net) #
- Sign Up / Log In:
- Go to https://notifysphere.net and access your dashboard.
- Create an Application:
- Navigate to Applications > Create New Application.
- Enter your application name and package/bundle ID.
- Note down your Application ID (
appId) and API Key (apiKey).
- Configure Firebase Cloud Messaging (FCM):
- In the Firebase Console, open your project settings > Service Accounts.
- Click Generate new private key and download the JSON file.
- In the NotifySphere dashboard, open your Application Settings and upload the Firebase Service Account JSON.
- Configure Apple Push Notification service (APNs) (for iOS):
- In Firebase Console > Project Settings > Cloud Messaging, upload your Apple Push Notifications Authentication Key (
.p8) and team ID.
- In Firebase Console > Project Settings > Cloud Messaging, upload your Apple Push Notifications Authentication Key (
2. Installation #
Add flutter_notify_sphere along with Firebase dependencies to your pubspec.yaml:
dependencies:
flutter:
sdk: flutter
flutter_notify_sphere: ^1.0.0
firebase_core: ^3.10.0
firebase_messaging: ^15.2.0
Run:
flutter pub get
3. Android Setup #
3.1 Firebase Configuration #
- Download
google-services.jsonfrom your Firebase project. - Place it into
android/app/google-services.json. - Add the Google Services plugin to your Gradle configuration:
- In
android/build.gradle(orandroid/settings.gradle):plugins { id 'com.google.gms.google-services' version '4.4.2' apply false } - In
android/app/build.gradle:plugins { id 'com.android.application' id 'com.google.gms.google-services' }
- In
3.2 Permissions (AndroidManifest.xml) #
In android/app/src/main/AndroidManifest.xml, add the following permissions inside the <manifest> tag:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Required permissions -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<!-- Optional: Only if useLocationData: true is passed to registerUser() -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application ...>
<!-- Optional default notification icon & color -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@mipmap/ic_launcher" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="notify_sphere_channel" />
...
</application>
</manifest>
3.3 Custom Notification Sound (Android) #
- Place your audio file (e.g.
custom_sound.mp3or.wav) into:android/app/src/main/res/raw/custom_sound.mp3 - When creating a notification in the NotifySphere dashboard, enter
custom_sound(without the file extension) in the sound field.
3.4 Rich Images (Android) #
Images sent in the notification (imageUrl or image in data payload) are automatically downloaded and displayed as BigPictureStyle notifications.
4. iOS Setup #
4.1 Firebase Configuration #
- Download
GoogleService-Info.plistfrom your Firebase Console. - Open your project in Xcode (
open ios/Runner.xcworkspace). - Drag and drop
GoogleService-Info.plistinto theRunnerfolder (ensure Copy items if needed is checked).
4.2 Xcode Capabilities #
In Xcode, select the Runner target > Signing & Capabilities:
- Click + Capability and add Push Notifications.
- Click + Capability and add Background Modes. Check:
- ✅ Remote notifications
- ✅ Background fetch
- Click + Capability and add App Groups:
- Add an App Group named:
group.<your.bundle.id>(e.g.group.com.example.myapp).
- Add an App Group named:
4.3 iOS Notification Service Extension (Background Delivery & Image Tracking) #
Important
iOS does not execute Flutter/Dart code when the app is in the background or terminated upon receiving a notification. To track delivery confirmations and show rich image attachments when the app is closed, you must configure an iOS Notification Service Extension.
Step 1: Create the Extension Target
- In Xcode, choose File > New > Target....
- Select Notification Service Extension and click Next.
- Name the extension (e.g.
ImageNotification), ensure language is Swift, and click Finish. - When prompted to activate scheme, click Cancel.
Step 2: Configure the App Group on the Extension
- Select your new
ImageNotificationtarget > Signing & Capabilities. - Click + Capability > App Groups.
- Check the exact same App Group you created for the Runner target (
group.<your.bundle.id>).
Step 3: Add NotifySphereExtension.swift
Inside your extension folder (e.g. ios/ImageNotification/), create a new Swift file named NotifySphereExtension.swift and paste the following code:
import Foundation
import UserNotifications
import os.log
public class NotifySphereExtension {
/// Called inside the Notification Service Extension to confirm delivery with NotifySphere
public static func didReceive(_ request: UNNotificationRequest, completion: @escaping () -> Void) {
let userInfo = request.content.userInfo
// Extract required notification identifier
guard let notificationId = userInfo["notification_id"] as? String ?? userInfo["gcm.message_id"] as? String else {
completion()
return
}
// Dynamically resolve the parent App Group suite
guard let extensionBundleId = Bundle.main.bundleIdentifier else {
completion()
return
}
let components = extensionBundleId.components(separatedBy: ".")
guard components.count >= 2 else {
completion()
return
}
let parentBundleId = components.dropLast().joined(separator: ".")
let dynamicAppGroup = "group.\(parentBundleId)"
guard let groupDefaults = UserDefaults(suiteName: dynamicAppGroup) else {
completion()
return
}
// Retrieve credentials saved by flutter_notify_sphere
let appId = groupDefaults.string(forKey: "app_id") ?? ""
let apiKey = groupDefaults.string(forKey: "api_key") ?? ""
let subscriptionId = groupDefaults.string(forKey: "subscription_id") ?? ""
guard !appId.isEmpty else {
completion()
return
}
// Construct delivery confirmation endpoint
let urlString = "https://api.notifysphere.in/client/\(appId)/delivery/confirm"
guard let url = URL(string: urlString) else {
completion()
return
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
if !apiKey.isEmpty {
urlRequest.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
}
let body: [String: Any] = [
"notification_id": notificationId,
"subscription_id": subscriptionId,
"platform": "ios"
]
urlRequest.httpBody = try? JSONSerialization.data(withJSONObject: body)
let task = URLSession.shared.dataTask(with: urlRequest) { _, _, _ in
completion()
}
task.resume()
}
}
Step 4: Implement NotificationService.swift
In the same extension folder, replace the contents of NotificationService.swift with:
import UIKit
import UserNotifications
import os.log
class NotificationService: UNNotificationServiceExtension {
private var contentHandler: ((UNNotificationContent) -> Void)?
private var bestAttemptContent: UNMutableNotificationContent?
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.contentHandler = contentHandler
bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
guard let bestAttemptContent = bestAttemptContent else {
contentHandler(request.content)
return
}
let userInfo = request.content.userInfo
// 1. Confirm delivery with NotifySphere
NotifySphereExtension.didReceive(request) {
NSLog("NotifySphere delivery confirmed in extension")
}
// 2. Custom Sound
if let aps = userInfo["aps"] as? [String: Any],
let soundName = aps["sound"] as? String {
bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: soundName))
} else if let soundName = userInfo["sound"] as? String {
bestAttemptContent.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: soundName))
} else {
bestAttemptContent.sound = .default
}
// 3. Expandable Image Attachment
if let imageUrl = userInfo["imageUrl"] as? String ?? userInfo["image"] as? String,
let url = URL(string: imageUrl) {
downloadImage(from: url) { attachment in
if let attachment = attachment {
bestAttemptContent.attachments = [attachment]
}
contentHandler(bestAttemptContent)
}
} else {
contentHandler(bestAttemptContent)
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler,
let bestAttemptContent = bestAttemptContent {
contentHandler(bestAttemptContent)
}
}
private func downloadImage(from url: URL, completion: @escaping (UNNotificationAttachment?) -> Void) {
URLSession.shared.downloadTask(with: url) { location, _, _ in
guard let location = location else {
completion(nil)
return
}
let tempDir = URL(fileURLWithPath: NSTemporaryDirectory())
let fileURL = tempDir.appendingPathComponent(UUID().uuidString + ".jpg")
try? FileManager.default.moveItem(at: location, to: fileURL)
let attachment = try? UNNotificationAttachment(
identifier: UUID().uuidString,
url: fileURL,
options: nil
)
completion(attachment)
}.resume()
}
}
4.4 Custom Notification Sound (iOS) #
- Add your audio file (e.g.
alert.caf,alert.wav, oralert.aiff) to Xcode. - In the file inspector, check both
RunnerandImageNotificationtarget memberships. - In NotifySphere dashboard, enter
alert.cafin the sound field.
5. Flutter Dart Integration #
5.1 Initialize in main.dart #
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_notify_sphere/flutter_notify_sphere.dart';
/// Top-level background message handler
@pragma('vm:entry-point')
Future<void> backgroundMessageHandler(RemoteMessage message) async {
debugPrint('Background message received: ${message.data}');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
// Initialize NotifySphere
await FlutterNotifySphere().initialize(
showForegroundNotification: true,
onForegroundMessage: (RemoteMessage message) {
debugPrint('Foreground notification: ${message.data}');
},
onNotificationClick: (RemoteMessage? message) {
debugPrint('Notification clicked: ${message?.data}');
// Handle navigation or in-app routing here
},
backgroundMessageHandler: backgroundMessageHandler,
);
runApp(const MyApp());
}
5.2 Register User & Device #
Call registerUser when your user logs in or on your app's home screen:
await FlutterNotifySphere().registerUser(
appId: "YOUR_NOTIFYSPHERE_APP_ID", // From notifysphere.net
apiKey: "YOUR_NOTIFYSPHERE_API_KEY", // From notifysphere.net
userData: {
"applicationUserId": "user_12345",
"name": "Jane Doe",
"email": "jane@example.com",
// Any custom metadata you want to associate with this subscriber
},
useLocationData: false, // Set to true to include lat/long and reverse-geocoded address
onFcmToken: (String? fcmToken) {
debugPrint("Device FCM Token: $fcmToken");
},
onRegistrationComplete: (String? subscriptionId) {
debugPrint("Registered with NotifySphere! Subscription ID: $subscriptionId");
},
onError: (Object error) {
debugPrint("Registration error: $error");
},
);
6. NotifySphere Payload Reference #
Notifications sent from the NotifySphere panel or REST API deliver the following data structure:
| Key | Type | Description |
|---|---|---|
notification_id |
String |
Unique tracking ID generated by NotifySphere for delivery and click tracking. |
title |
String |
The notification title. |
body |
String |
The notification body text. |
imageUrl |
String? |
Optional URL of the image attachment. |
sound |
String? |
Optional custom sound file name (e.g. notification_sound). |
custom_key |
dynamic |
Any additional key-value pairs configured in the NotifySphere campaign. |
7. Verifying Integration #
- Registration: Run your application. Inspect debug console logs for:
Registration Complete: <subscription_id> - Delivery Confirmation: Send a test notification from the NotifySphere panel:
- Check the NotifySphere campaign dashboard: the Delivered count will increment in real time.
- Click Tracking: Tap the notification banner:
- The Clicked count will increment in the campaign dashboard.
- iOS Console Logs: On macOS, open Console.app, select your test device, and filter for
NotifySphereExtensionor categoryFlutterNotifySphere.
License #
This project is licensed under the MIT License - see the LICENSE file for details.