Setting Up the Push Notification SDK
What you'll learn
- Generating the Push Notification SDK with FCM and/or OneSignal providers
- Configuring Firebase on Android and iOS
- Requesting permission and retrieving the device token
- Handling tap-driven navigation from a notification
- Creating Android notification channels and simulating topic subscriptions
Prerequisites
- An existing Archipelago monorepo (see Monorepo Scaffolding)
- A Firebase project (for FCM) or a OneSignal account (for OneSignal)
Step 1: Generate the Push Notification SDK
The Push Notification SDK is a Free tier brick.
archipelago generate push_notification_sdkYou will be prompted for:
- includeFcm —
trueto include the Firebase Cloud Messaging provider - includeOneSignal —
trueto include the OneSignal provider
At least one provider must be enabled. Both can be enabled simultaneously, though using a single provider is the common setup.
Or use a config file:
{
"includeFcm": true,
"includeOneSignal": false
}archipelago generate push_notification_sdk --config push_notification_config.jsonStep 2: Understand the Generated Structure
features/
└── push_notification/
├── push_notification_api/ # PushNotificationSDK contract
├── push_notification_impl/ # Provider-agnostic orchestration
├── push_notification_fcm_impl/ # FCM-specific implementation (if includeFcm)
└── push_notification_onesignal_impl/ # OneSignal implementation (if includeOneSignal)Your app code and other features depend only on push_notification_api. Swapping or adding providers is a DI change, not an application change.
Step 3: Configure FCM on Android
Download
google-services.jsonfrom the Firebase console and place it at:apps/my_app/android/app/google-services.jsonVerify
android/app/build.gradleapplies the plugin (generated automatically):groovyapply plugin: 'com.google.gms.google-services'Create a default notification channel (required on Android API 26+):
kotlin// apps/my_app/android/app/src/main/kotlin/.../MainActivity.kt override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "default", "General", NotificationManager.IMPORTANCE_DEFAULT, ) getSystemService(NotificationManager::class.java) .createNotificationChannel(channel) } }
Step 4: Configure FCM on iOS
Download
GoogleService-Info.plistfrom the Firebase console and place it at:apps/my_app/ios/Runner/GoogleService-Info.plistInitialize Firebase in
AppDelegate.swift:swiftimport FirebaseCore @main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { FirebaseApp.configure() return super.application(application, didFinishLaunchingWithOptions: launchOptions) } }In Xcode, enable the following capabilities for the
Runnertarget:- Push Notifications
- Background Modes → Remote notifications
Step 5: Configure OneSignal (if includeOneSignal is true)
Create an app at onesignal.com and copy the App ID.
Initialize OneSignal before
runAppinmain.dart:dartvoid main() async { WidgetsFlutterBinding.ensureInitialized(); OneSignal.initialize('<YOUR_ONESIGNAL_APP_ID>'); runApp(const MyApp()); }In Xcode, enable the same capabilities as FCM:
- Push Notifications
- Background Modes → Remote notifications
Step 6: Request Permission and Get the Token
In your post-launch initializer or onboarding flow:
final pushSDK = getIt<PushNotificationSDK>();
// Request system permission (shows the OS prompt on first call)
await pushSDK.requestPermission();
// Retrieve the device token for your backend
final token = await pushSDK.getToken();
if (token != null) {
await userRepository.registerDeviceToken(token);
}Step 7: Handle Tap-Driven Navigation
When a user taps a notification, the SDK emits on tapStream. Wire this in your shell app after the router is ready:
// apps/my_app/lib/host_app_widget.dart
@override
void initState() {
super.initState();
getIt<PushNotificationSDK>().tapStream.listen((notification) {
if (notification.route != null) {
router.push(RouteUri.parse(notification.route!));
}
});
}The notification.route value is populated from a route key in the notification data payload, so your backend controls where deep links land.
Step 8: Simulate Topic Subscriptions with OneSignal
OneSignal does not have a native topic API like FCM. The SDK simulates topics using tags:
// Subscribe the user to the "premium" topic
await pushSDK.subscribeToTopic('premium');
// Internally calls: OneSignal.User.addTag('topic_premium', 'true')
// Unsubscribe
await pushSDK.unsubscribeFromTopic('premium');
// Internally calls: OneSignal.User.removeTag('topic_premium')Segment your OneSignal audiences by the topic_* tag keys to send targeted pushes.
Common Customizations
| Goal | What to do |
|---|---|
| Multiple notification channels (Android) | Create additional NotificationChannel instances with distinct IDs |
| Handle foreground notifications | Listen to foregroundStream on PushNotificationSDK |
| Send token to backend on refresh | Listen to tokenRefreshStream and re-register |
| Use APNs auth key instead of p12 | Upload the .p8 key in Firebase console → Project Settings → iOS app |
| Custom notification icon (Android) | Set com.google.firebase.messaging.default_notification_icon in AndroidManifest.xml |
Next Steps
- Set up Permission Guard to track the permissions added by push notification SDKs
- Configure the Launch Tracker to time your push SDK initialization in the post-launch phase