Flutter Quickstart
This guide walks you through integrating PushEngage push notifications into a Flutter application targeting both Android and iOS. Estimated time: 15 minutes.
After completing this guide, your app will receive push notifications on both platforms. See the Flutter SDK Reference for the full API.
1.0.0 bumps the underlying iOS native SDK to 1.0.0, which splits the SDK into two CocoaPods so notification extension targets no longer link app-only code. Existing iOS integrations need three changes:
Remove the Podfile build-setting override — delete the
APPLICATION_EXTENSION_API_ONLY = 'No'line from yourpost_installblock. It is no longer required.Switch extension target pods — in
ios/Podfile, replacepod 'PushEngage', '0.0.6'(or earlier) inside each extension target block withpod 'PushEngageExtension'. The mainRunnertarget continues to usepod 'PushEngage'(pulled transitively bypushengage_flutter_sdk).Update extension Swift code — in
NotificationService.swift(andNotificationViewController.swiftif present), replaceimport PushEngagewithimport PushEngageExtension, and thePushEngageclass prefix on extension API calls withPushEngageExtension. Method names are unchanged.// Before
import PushEngage
PushEngage.didReceiveNotificationExtensionRequest(request, bestContentHandler: bestContent)
PushEngage.serviceExtensionTimeWillExpire(request, content: bestAttemptContent)
PushEngage.getCustomUIPayLoad(for: notification.request)
// After
import PushEngageExtension
PushEngageExtension.didReceiveNotificationExtensionRequest(request, bestContentHandler: bestContent)
PushEngageExtension.serviceExtensionTimeWillExpire(request, content: bestAttemptContent)
PushEngageExtension.getCustomUIPayLoad(for: notification.request)
Dart API breaking change: deepLinkStream events now always carry data as a Map on both platforms. On Android the SDK previously delivered data as a JSON-encoded string — if your handler was decoding it manually, remove that step.
Prerequisites
- Flutter SDK 3.x or higher
- Android Studio (for Android builds) and/or Xcode (for iOS builds)
- Firebase account (create one free) — required for Android
- Apple Developer account with a valid App ID — required for iOS
- APNs certificate or key (create one) — required for iOS
- PushEngage account and App ID (see Get Your App ID)
Step 1 — Install the SDK
Add the dependency to pubspec.yaml:
dependencies:
pushengage_flutter_sdk: ^1.0.0
Then run:
flutter pub get
Step 2 — Android Setup
Firebase Cloud Messaging (FCM)
- Open the Firebase console and sign in.
- Click Add Project (or select an existing one).
- Click the Android icon to add an Android app.
- Enter your app's package name (found in
android/app/build.gradleunderapplicationId). - Download google-services.json and place it at
android/app/google-services.json. - Generate the Service Account JSON: Firebase console → Settings → Service accounts → Generate new private key.
- Retrieve the Sender ID: Firebase console → Settings → Cloud Messaging tab.
Apply the Google Services Gradle Plugin
google-services.json only takes effect when the Google Services Gradle plugin is applied. Add it in two places:
In your project-level
android/build.gradle, add the classpath underbuildscript→dependencies:android/build.gradlebuildscript {
dependencies {
classpath 'com.google.gms:google-services:4.4.0'
}
}In your app-level
android/app/build.gradle, apply the plugin:android/app/build.gradleplugins {
id "com.android.application"
id "kotlin-android"
id "com.google.gms.google-services"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin plugins.
id "dev.flutter.flutter-gradle-plugin"
}
Without this plugin, FCM registration fails and no Android push token is generated.
Set the Activity Base Class
PushEngage's runtime permission and subscribe calls require the host activity to be a ComponentActivity. Flutter's default FlutterActivity is not one, so requestNotificationPermission() and subscribe() fail with an INVALID_ACTIVITY error. For requestNotificationPermission() the Dart layer reports this as a silent false (no exception is thrown); subscribe() returns a failure result with the error attached. Make MainActivity extend FlutterFragmentActivity:
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity: FlutterFragmentActivity()
Connect to PushEngage Dashboard
- Log in to your PushEngage Dashboard.
- Navigate to Site Settings → Installation → Android SDK tab.
- Enter your Firebase Sender ID and upload the Service Account JSON.
- Click Update and copy the App ID.
Step 3 — iOS Setup
Open your iOS workspace in Xcode:
your_project_name/ios/Runner.xcworkspace
Enable Xcode Capabilities
- Select the root project and choose the Runner target.
- Go to Signing & Capabilities → + Capability → Push Notifications.
- Click + Capability → Background Modes, then check Remote notifications and Background fetch.
Connect iOS to PushEngage Dashboard
- PushEngage Dashboard → Site Settings → Installation → iOS SDK tab.
- Upload your APNs certificate or key.
- Copy the App ID.
Step 4 — Add Notification Service Extension (iOS)
Create the Extension
- In Xcode, go to File → New → Target → Notification Service Extension → Next.
- Name it
PushEngageNotificationServiceExtension→ Finish. Click Cancel when prompted to activate. - Set the new target's Deployment Target to iOS 12 or above.
Configure Podfile
Open ios/Podfile and add the extension target at the bottom of the file:
target 'PushEngageNotificationServiceExtension' do
use_frameworks!
pod 'PushEngageExtension'
end
Notification extensions link the companion PushEngageExtension pod, not the main PushEngage pod. PushEngageExtension is the extension-safe core that ships the three notification-extension APIs (didReceiveNotificationExtensionRequest, serviceExtensionTimeWillExpire, getCustomUIPayLoad). Linking the wrong pod can break push delivery on production builds.
Run in your ios/ directory:
pod repo update
pod install
Implement the Extension
Replace the auto-generated NotificationService.swift:
import UserNotifications
import PushEngageExtension
@available(iOSApplicationExtension 12.0, *)
class NotificationService: UNNotificationServiceExtension {
var contentHandler: ((UNNotificationContent) -> Void)?
var bestAttemptContent: UNMutableNotificationContent?
var request: UNNotificationRequest?
override func didReceive(
_ request: UNNotificationRequest,
withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
) {
self.request = request
self.contentHandler = contentHandler
self.bestAttemptContent = request.content.mutableCopy() as? UNMutableNotificationContent
if let bestContent = bestAttemptContent {
PushEngageExtension.didReceiveNotificationExtensionRequest(request, bestContentHandler: bestContent)
contentHandler(bestContent)
}
}
override func serviceExtensionTimeWillExpire() {
if let contentHandler = contentHandler,
let request = request,
let bestAttemptContent = bestAttemptContent {
guard let content = PushEngageExtension.serviceExtensionTimeWillExpire(
request, content: bestAttemptContent
) else {
contentHandler(bestAttemptContent)
return
}
contentHandler(content)
}
}
}
Step 5 — Add Notification Content Extension (iOS, Optional)
- In Xcode, go to File → New → Target → Notification Content Extension → Next.
- Name it
PushEngageNotificationContentExtension→ Finish. Click Cancel when prompted. - Set Deployment Target to iOS 12 or above.
- Add to
ios/Podfile:
target 'PushEngageNotificationContentExtension' do
use_frameworks!
pod 'PushEngageExtension'
end
Run pod install in the ios/ directory.
Step 6 — Add App Groups (iOS)
- Select the Runner target in Xcode → Signing & Capabilities → + Capability → App Groups.
- Add a new App Group:
group.com.yourcompany.yourapp. - In
ios/Runner/Info.plist, add:- Key:
PushEngage_App_Group_Key - Value:
group.com.yourcompany.yourapp
- Key:
- In
PushEngageNotificationServiceExtension/Info.plist, add the same key and value. - Select the extension target → enable the same App Group under Signing & Capabilities.
Step 7 — Initialize the SDK
In lib/main.dart:
import 'package:flutter/material.dart';
import 'package:pushengage_flutter_sdk/pushengage_flutter_sdk.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
void initState() {
super.initState();
_initPushEngage();
}
Future<void> _initPushEngage() async {
await PushEngage.setAppId('YOUR_APP_ID');
// Trigger the system permission prompt; on grant, the SDK auto-subscribes.
await PushEngage.requestNotificationPermission();
}
Widget build(BuildContext context) {
return const MaterialApp(
title: 'My App',
home: HomeScreen(),
);
}
}
For iOS distribution, initialize PushEngage in ios/Runner/AppDelegate.swift:
import Flutter
import UIKit
import PushEngage
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override init() {
super.init()
PushEngage.swizzleInjection(isEnabled: true)
}
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self
}
GeneratedPluginRegistrant.register(with: self)
PushEngage.setBadgeCount(count: 0)
PushEngage.setNotificationWillShowInForegroundHandler { notification, completion in
if notification.contentAvailable == 1 {
completion(nil)
} else {
completion(notification)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Replace YOUR_APP_ID with the App ID from Steps 2 or 3.
Step 8 — Send a Test Notification
- Build and run on a physical device (Android or iOS).
- Accept the notification permission prompt.
- In the PushEngage Dashboard → Campaign → Push Broadcasts → Create New Push Broadcast → send a test.
Troubleshooting
Android: notifications not received
Verify google-services.json is at android/app/google-services.json and that the Sender ID in the PushEngage Dashboard matches Firebase.
iOS: build sandboxing error
In Xcode → Build Settings → set User Script Sandboxing to No.