notify_mvp 1.0.0
notify_mvp: ^1.0.0 copied to clipboard
Official Flutter SDK for NotifyMVP — a OneSignal-like push notification platform. Handles FCM token registration, auto-refresh, and device identification with zero boilerplate.
notify_mvp — Flutter SDK #
Official Flutter SDK for NotifyMVP — a self-hosted push notification platform.
What it does #
- Requests FCM (Firebase Cloud Messaging) permission
- Registers the device token with your NotifyMVP backend
- Auto re-registers when the FCM token refreshes
- Persists a stable
deviceIdacross app restarts using secure storage - Retries failed registrations with exponential backoff
Installation #
Add to your app's pubspec.yaml:
dependencies:
notify_mvp: ^1.0.0
firebase_core: ^3.0.0
firebase_messaging: ^15.0.0
Run:
flutter pub get
Setup #
1. Configure Firebase #
Follow the FlutterFire setup guide to:
- Create a Firebase project
- Add your Android/iOS apps
- Download
google-services.json(Android) andGoogleService-Info.plist(iOS) - Run
flutterfire configureto generatefirebase_options.dart
2. Android — android/app/build.gradle (or build.gradle.kts) #
A. Apply Google Services Plugin:
plugins {
id 'com.google.gms.google-services'
}
B. Enable Core Library Desugaring (Required for High-Priority Notifications):
Why is this required?
NotifyMVP Flutter SDK delivers high-priority Heads-Up system notifications (pop-up banner with sound & vibration) using Java 8+ Android APIs. Enabling core library desugaring allows older Android versions to process these APIs without crashing.
If using Groovy (build.gradle):
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
}
}
dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4'
}
If using Kotlin DSL (build.gradle.kts):
android {
...
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
}
C. Add Default Notification Channel to android/app/src/main/AndroidManifest.xml:
Inside your <application> tag, add this <meta-data> entry so Android FCM assigns background notifications to the High-Priority Pop-up Channel (enabling top pop-up banners on mobile home screen):
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="notifymvp_heads_up_channel" />
3. iOS — ios/Runner/Info.plist #
Add background modes for push notifications:
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
Usage #
Initialize in main.dart #
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:notify_mvp/notify_mvp.dart';
import 'firebase_options.dart'; // generated by flutterfire configure
// Background handler — must be a top-level function
@pragma('vm:entry-point')
Future<void> _backgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 1. Initialize Firebase
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// 2. Register background handler
FirebaseMessaging.onBackgroundMessage(_backgroundHandler);
// 3. Initialize NotifyMVP — that's it!
await NotifyMVP.initialize(
appId: 'app_a3f9bc12', // From dashboard → Projects
apiKey: 'your_api_key', // From dashboard → Projects
baseUrl: 'https://your-app.vercel.app', // Your NotifyMVP URL
);
runApp(const MyApp());
}
Handle foreground notifications #
@override
void initState() {
super.initState();
// Foreground messages
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Title: ${message.notification?.title}');
print('Body: ${message.notification?.body}');
});
}
Deep link / Launch URL (OneSignal-style) #
Dashboard Launch URL arrives as data['url'].
// Cold start (app was killed)
final initial = await NotifyMVP.getInitialNotification();
if (initial?.url != null) {
// GoRouter.of(context).go(initial!.url!);
}
// Background → tap
NotifyMVP.onNotificationOpened().listen((payload) {
if (payload.url != null) {
// navigate using payload.url
}
});
Manual registration #
// Re-register (e.g. after user logs in)
final result = await NotifyMVP.register();
if (result.isSuccess) {
print('Registered! deviceId: ${result.data?['deviceId']}');
} else {
print('Failed: ${result.error}');
}
API Reference #
NotifyMVP.initialize(...) #
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
appId |
String |
✅ | — | Your NotifyMVP App ID |
apiKey |
String |
✅ | — | Your NotifyMVP API Key |
baseUrl |
String |
✅ | — | NotifyMVP deployment URL |
debugLogging |
bool |
❌ | false |
Enable console logs |
autoRegister |
bool |
❌ | true |
Register on init |
Returns NotifyResult — check .isSuccess and .error.
NotifyMVP.register() #
Manually register or re-register the device. Returns NotifyResult.
NotifyMVP.fcmToken #
String? — the current FCM token. Null until initialized.
NotifyMVP.isInitialized #
bool — whether the SDK has been initialized.
NotifyConfig (advanced) #
Pass a custom config for fine-grained control:
await NotifyMVP.initialize(
appId: 'app_a3f9bc12',
apiKey: 'your_api_key',
baseUrl: 'https://your-app.vercel.app',
config: NotifyConfig(
appId: 'app_a3f9bc12',
apiKey: 'your_api_key',
baseUrl: 'https://notify.earnslash.com',
debugLogging: false,
requestTimeout: Duration(seconds: 10),
maxRetries: 3,
),
);
How it works #
Flutter App NotifyMVP Backend Firebase FCM
│ │ │
│── NotifyMVP.initialize() ───────►│ │
│ │ │
│◄── Request FCM permission ───────┤ │
│ │ │
│── getToken() ────────────────────┼────────────────────────►│
│◄── FCM token ────────────────────┼─────────────────────────│
│ │ │
│── POST /api/device/register ────►│ │
│ { appId, apiKey, fcmToken, │ │
│ deviceId, platform } │ │
│◄── { success: true } ────────────│ │
│ │ │
│ Token saved in DB │
│ │ │
│ [later — dashboard send] │ │
│ │── FCM multicast ───────►│
│◄── Push notification ────────────┼─────────────────────────│
Security notes #
- The
apiKeyis embedded in your app binary — this is expected and intentional (same model as OneSignal, Firebase, etc.) - The API key only allows device registration — it cannot read data, send notifications, or access other projects
- Sending notifications requires the dashboard (server-side only) — the apiKey alone cannot trigger sends
deviceIdis stored in encrypted secure storage (flutter_secure_storage)
Platform support #
| Platform | Status |
|---|---|
| Android | ✅ |
| iOS | ✅ |
| Flutter Web | ❌ (web push not in scope for MVP) |
| macOS | ⚠️ (untested) |
| Windows/Linux | ❌ (FCM not supported) |
Troubleshooting #
"FCM token unavailable — notification permission may be denied"
→ User denied notification permission. Show a rationale UI and call NotifyMVP.register() again after they grant permission.
"Invalid appId or apiKey" → Double-check your App ID and API Key in the NotifyMVP dashboard → Projects.
"Request failed after 3 attempts"
→ Check your baseUrl is correct and the server is reachable.
Token not refreshing
→ The SDK listens to FirebaseMessaging.instance.onTokenRefresh automatically. Make sure NotifyMVP.initialize() is called before runApp().