Docs Add PusherHub to your app
Flutter push notifications: add the SDK step by step
Android and iOS, every step with a picture from a real Flutter app: the SDK file, the packages, the Gradle and Xcode settings, and the code in main.dart.
12 min read
All guides
- Get started with PusherHub
- Create a Firebase project
- Get your Firebase service account file
- Find your Sender ID
- iOS push notifications: add your Apple push key to Firebase
- Add push notifications to your app
- Flutter
- Android (Kotlin)
- Java
- React Native
- iOS (Swift)
- Unity
- Send a test push notification
- Send your first push notification
Download pusher_hub.dart
In the dashboard, open
Applications
your app
SDK Integration
and pick Flutter ①. Choose where your app runs — Android + iOS ② here — and click Download pusher_hub.dart ③. It goes in your lib/ folder ④.
Every step below is on this tab too, with your own keys already filled in — copy the code from there.
Put the three files in your project
pusher_hub.dart① goes inlib/, next tomain.dart.- Android:
google-services.json② goes inandroid/app/. - iOS:
GoogleService-Info.plist③ goes inios/Runner/, and Xcode has to know about it: openios/Runner.xcworkspace, drag the file onto the Runner folder and tick the Runner target.
Add the packages
Add these to dependencies: in pubspec.yaml ①, then run flutter pub get ②. pusher_hub.dart needs every one of them to build, even the ones for features you don't use.
pubspec.yaml
dependencies: firebase_core: ^4.14.0 firebase_messaging: ^16.6.0 http: ^1.6.0 shared_preferences: ^2.3.0 device_info_plus: ^11.1.1 package_info_plus: ^8.1.2 flutter_timezone: ^5.1.0 geolocator: ^13.0.1 geocoding: ^5.0.0 url_launcher: ^6.3.1 webview_flutter: ^4.10.0
Android: add the Google services plugin
Open android/settings.gradle.kts. Inside plugins { }, add ①:
id("com.google.gms.google-services") version "4.4.2" apply false
Android: turn the plugin on for the app
Open android/app/build.gradle.kts. Inside plugins { }, add id("com.google.gms.google-services") ① — above the Flutter plugin, which has to stay last.
Android: add to AndroidManifest.xml
Open android/app/src/main/AndroidManifest.xml and add these to the manifest you already have — don't replace it.
- The permissions ①.
ACCESS_COARSE_LOCATIONis only needed if you turn on GPS state and city (step 13). - A second
<intent-filter>② inside theMainActivity<activity>, so a tap on a notification opens your app and is counted. - An
<intent>in<queries>③, so links in notifications and in-app messages can open in the browser on Android 11 and later.
iOS: turn on push notifications in Xcode
Open ios/Runner.xcworkspace in Xcode and select the Runner target. On Signing & Capabilities ①, choose your paid Apple Developer Team, then click + Capability ② and add Background Modes — tick Remote notifications ③ — and Push Notifications ④.
Firebase needs iOS 15 or later: set platform :ios, '15.0' at the top of ios/Podfile, and Minimum Deployments to 15.0 on the Runner target's General tab.
iOS: add to Info.plist
Open ios/Runner/Info.plist and add, inside <dict>:
- The location text ①. Keep it even if you never turn location on — the location package is always in your app, and the App Store can warn about it otherwise.
UIBackgroundModes② withremote-notification. (Ticking Remote notifications in step 7 adds this for you.)
ios/Runner/Info.plist
<key>NSLocationWhenInUseUsageDescription</key>
<string>Used to resolve your approximate state/region so notifications can be targeted correctly.</string>
<key>UIBackgroundModes</key>
<array>
<string>remote-notification</string>
</array>
iOS: show pictures in notifications (recommended)
An iPhone doesn't download a notification's image by itself — without this step it shows only the title and text. In Xcode, with ios/Runner.xcworkspace open:
-
File
New
Target…
, pick Notification Service Extension, name it
NotificationService, click Finish, then Don't Activate. - On the NotificationService target's General tab, set Minimum Deployments to 15.0.
- On the Runner target's Build Phases, drag Embed Foundation Extensions up, above Thin Binary. Skip this and the build fails with "Cycle inside Runner".
- On the project's Info tab, under Configurations, set the NotificationService row to Generated for Debug, Release and Profile. Then in the NotificationService target's Build Settings, set Marketing Version to
$(FLUTTER_BUILD_NAME)and Current Project Version to$(FLUTTER_BUILD_NUMBER). - Replace everything in
ios/NotificationService/NotificationService.swiftwith the file on your SDK Integration tab.
The simulator can't draw notification pictures, so check this on a real iPhone.
iOS, optional: clear the badge
Only if your notifications set a badge number on the app icon. iOS keeps it until the app clears it, and Dart can't, so add these to ios/Runner/AppDelegate.swift: the import ① and, before the return, the lines that clear it each time the app opens ②.
Start PusherHub in main.dart
Open lib/main.dart and add the code from your SDK Integration tab — your keys and server are already filled in. (In the picture they're placeholders.)
- The import ①:
import 'pusher_hub.dart'; PusherHub(…)② with your App key, Public key and server, outsidemain().registerDevice()③ gets the phone's push token, asks the user to allow notifications and adds the phone to PusherHub.collectDeviceMetadata()after it is optional: it adds the phone's brand, model, language and country.PusherHubMessageHost④ around your home screen — optional: it shows your in-app messages when the app opens. Leave it out if you only send notifications.
Optional: log the user in
If your app has its own sign-in, tell PusherHub who's using the phone. Then you can send to one person by your own user ID. It's remembered, even after the app restarts, until logout().
// When someone signs in: await pusherHub.login(currentUserId); // When they sign out: await pusherHub.logout();
Optional: open a screen when a notification is tapped
Put your own data in Custom Data when you create a notification — say {"campaign_id": "232"} — and your app gets it back whenever someone taps that notification, whether the app was open, in the background or closed. Add onNotificationOpened to the PusherHub(…) from step 11, and give your MaterialApp a navigatorKey so it can open the screen:
final navigatorKey = GlobalKey<NavigatorState>();
final pusherHub = PusherHub(
appKey: 'YOUR_APP_KEY',
publicKey: 'YOUR_PUBLIC_KEY',
baseUrl: 'YOUR_SERVER',
onNotificationOpened: (data) {
final campaignId = data['campaign_id'];
if (campaignId != null) {
navigatorKey.currentState?.pushNamed('/campaign', arguments: campaignId);
}
},
);
// ...and: MaterialApp(navigatorKey: navigatorKey, ...)
Every value arrives as text ("232"). If your app starts on a splash screen that moves on by itself, keep the data and open the screen after it. This needs pusher_hub.dart from 24 October 2026 or later — download it again if yours is older.
Optional: GPS state and city
PusherHub already estimates each phone's state from its internet address. If you need it from GPS instead, turn location on — the app then asks for location once, and if the user says no, only state and city are skipped. It needs the location permission from steps 6 and 8.
await pusherHub.collectDeviceMetadata(resolveLocation: true);
Run the app on a phone
Run flutter run on an Android phone or emulator with Google Play, or on a real iPhone (the iOS simulator can't get push notifications). Tap Allow when it asks about notifications. Within a few seconds the phone shows up in PusherHub under Devices.