Push notifications
Receive Grovs push campaigns in a Flutter app: pass the token, count taps, follow campaign links and show images.
The plugin counts taps on Grovs push campaigns and delivers each tapped push to onDeeplinkReceived, like a deep link. Campaigns come from the dashboard, a journey or the server API. To create them, see Push campaigns.
Tap handling, isGrovsNotification and handleNotificationOpen need plugin 3.2.0 or later.
Push needs a physical device. It doesn't work in the iOS Simulator, and on Android it needs a device or emulator with Google Play services.
iOS
1. Upload your APNs key to Grovs
- In your Apple Developer account, create a key with Apple Push Notifications service (APNs) enabled.
- Download the
.p8file. Note the Key ID and your Team ID. - In the Grovs dashboard, open SDK Setup → iOS. In the Push Notifications step, upload the
.p8file and enter the Key ID and Team ID.
Test sends through the APNs sandbox, production through production APNs. Builds run from Xcode or flutter run get sandbox tokens, so set GrovsUseTestEnvironment to true for them and false for TestFlight and App Store builds. APNs rejects a token from the other environment.
2. Register for pushes
In Xcode, select the Runner target, open Signing & Capabilities and add Push Notifications. Grovs pushes are regular alerts, so Grovs doesn't need the Remote notifications background mode.
The app must ask for permission and register with APNs, or it never gets a token. firebase_messaging registers for you; call its requestPermission() for the permission. Without it, add this to ios/Runner/AppDelegate.swift:
// In application(_:didFinishLaunchingWithOptions:), before returning
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in }
application.registerForRemoteNotifications()3. Set the notification center delegate
The plugin shows and counts Grovs pushes through the system notification center. Set the delegate in ios/Runner/AppDelegate.swift:
import UserNotifications
// In application(_:didFinishLaunchingWithOptions:), before returning
UNUserNotificationCenter.current().delegate = selfFlutterAppDelegate passes the notification center callbacks on to plugins. Without this line, taps on Grovs pushes aren't counted and Grovs pushes don't show while the app is open.
4. Token
Once the app is registered, the plugin passes the APNs token to Grovs. You don't need setPushToken on iOS. Calling it still works, but causes one extra attributes update.
Foreground pushes
While the app is open, the plugin shows Grovs pushes as a banner, in the notification list and with sound (alert and sound on iOS 13). Pushes from other providers are left to your own code. Grovs pushes also show while the SDK is disabled; that is display only, nothing is tracked.
With firebase_messaging 16.x, one more step is needed. That version answers every notification before Grovs can, so nothing shows by default. Upgrade to firebase_messaging 17 or later, or call this once after Firebase starts:
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);With 16.x this also shows other Firebase pushes while the app is open.
Campaign images (optional)
Campaign images need a Notification Service Extension. Add one as a new target in Xcode, then add the Grovs extension library to it in ios/Podfile:
target 'NotificationService' do
pod 'GrovsNotificationService', '~> 3.1'
endMake the extension's principal class inherit from the Grovs one:
import GrovsNotificationService
class NotificationService: GrovsNotificationServiceExtension {}Use JPEG, PNG or GIF images. The iOS Simulator never runs the extension, so test on a device. If you already have an extension, see iOS campaign images.
Android
Android push needs Firebase Cloud Messaging in your app. Add firebase_messaging, or add com.google.firebase:firebase-messaging and the google-services plugin yourself. The Grovs SDK doesn't bring Firebase with it.
1. Upload your Firebase credentials
In the Firebase Console, open Project Settings → Service Accounts and generate a private key. In the Grovs dashboard, open SDK Setup → Android and, in the Push Notifications step, upload the JSON key and enter your Firebase Project ID.
On Android 13 and later, the app must also ask for the POST_NOTIFICATIONS permission. firebase_messaging declares it and requestPermission() asks for it.
2. Token
When Firebase is in your app, the plugin fetches the first FCM token for you, and again each time the SDK is turned on with setSDK(true). With firebase_messaging, its messaging service receives token refreshes, so pass them on:
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:grovs_flutter_plugin/grovs.dart';
final grovs = Grovs();
FirebaseMessaging.instance.onTokenRefresh.listen((newToken) {
grovs.setPushToken(newToken);
});
final token = await FirebaseMessaging.instance.getToken();
if (token != null) await grovs.setPushToken(token);3. Foreground pushes
The plugin declares the Grovs messaging service at priority -1, so firebase_messaging handles messages first when your app uses it.
- Without
firebase_messaging: Grovs draws its pushes while the app is open. - With
firebase_messaging: Grovs doesn't draw pushes while the app is open. Draw them in youronMessagehandler, and useisGrovsNotificationto tell them apart:
FirebaseMessaging.onMessage.listen((message) async {
if (await grovs.isGrovsNotification(message.data)) {
// Show it yourself, for example with flutter_local_notifications
}
});A notification you draw yourself doesn't carry the Grovs data, so its tap isn't counted. In its tap handler, call grovs.handleNotificationOpen(message.data).
In the background, Firebase shows the push. The notification channel and small icon come from your manifest; see Android push notifications for the meta-data keys.
4. Taps
Taps on Grovs pushes that Grovs or the system drew are counted with no code. For notifications you draw yourself, see step 3.
What your app receives
A tapped Grovs push arrives on onDeeplinkReceived:
- Campaign with a link:
linkis the resolved link.datais the link's data merged with the push's keys; on a clash the push's value wins. - Campaign without a link:
linkisnullanddatais the push's data.
The push's keys are your campaign's custom data plus grovs_campaign_id, and grovs_link and grovs_image when set. On Android the push's values are strings.
grovs.onDeeplinkReceived.listen((details) {
final link = details.link; // null for a push without a link
final screen = details.data?['screen'];
// route to the screen your campaign or link points at
});A tap on a push with a Grovs link counts once, as a campaign open. The link isn't also counted as a link open.
While the SDK is disabled, taps aren't delivered or reported. On iOS they're dropped. On Android, a tap that opened the app is handled when setSDK(true) is called while the app is still in the foreground.
Handling taps yourself
handleNotificationOpen(data) counts a tap and opens the push's link. Use it only when the plugin can't see the tap: on Android when you draw the notification yourself, or when the native tap path can't be used.
final handled = await grovs.handleNotificationOpen(message.data);- Returns
truewhen the data belongs to a Grovs push and the tap was handled. - Returns
falsefor other pushes and while the SDK is disabled. isGrovsNotification(data)checks without handling. A push is a Grovs push when it has a non-emptygrovs_campaign_id.
Call handleNotificationOpen only from a tap handler, never when a push is delivered, or each delivery counts as an open. Use one path per platform. On iOS, setting the notification center delegate and also calling handleNotificationOpen counts every tap twice.
Testing
Create a campaign whose audience is your own user ID (the value you pass to setUserIdentifier). Send it to a build on the matching environment and tap it. The open shows in the campaign report.
Asking for permission from an in-app message
An in-app message can carry a button that asks for push permission. On Android 13 and later this needs POST_NOTIFICATIONS in your manifest; firebase_messaging already declares it.
See also
- Push campaigns: create and send campaigns
- iOS push notifications and Android push notifications: native behavior underneath
- API reference: every push call
- Upgrading to 3.2