flutter_local_notifications for display and action buttons.
Track your campaigns: CalltrackMetric(opened)and track action clicks yourself. Campaigns can be Native (OS-rendered) or Custom (app-rendered). Zixflow sendsZixflow-Delivery-ID,Zixflow-Delivery-Token, plus routing keys (deeplink_url,action_buttons,template_id). For delivery, opens, action-button details, wire format, and Journeys event names (Delivered/Opened/Clicked), see Push Notification Tracking. The reference implementation islib/push_handlers.dartin sdk-examples/flutter.
Prerequisites
- Configure FCM in the Zixflow dashboard — add your Firebase / FCM credentials
- Firebase project — create a project and add both iOS and Android apps
- Identify users — call
identify()before sending notifications so tokens attach to a profile - Test on physical devices — iOS Simulator cannot receive remote pushes
Platform-Specific Requirements
iOS
- iOS 13.0+, Xcode 14.0+
- Push Notifications capability enabled
- Optional Notification Service Extension for rich push and reliable delivered metrics
- Physical device for testing
Android
- API 21+ (Android 5.0)
google-services.jsoninandroid/app/POST_NOTIFICATIONSfor Android 13+ (API 33+)coreLibraryDesugaring(required byflutter_local_notifications)
Step 1: Firebase Project Setup
- Create or open a project at Firebase Console
- Add an iOS app (bundle ID), download
GoogleService-Info.plist→ios/Runner/ - Add an Android app whose package name matches
applicationIdinandroid/app/build.gradle, downloadgoogle-services.json→android/app/
applicationId must match the package name registered in Firebase, or FCM token registration fails.
Step 2: Add Dependencies
Step 3: Initialize Firebase and Zixflow
Nativegoogle-services.json / GoogleService-Info.plist is enough. FlutterFire (firebase_options.dart) is optional.
Step 4: Handle Push Notifications (Dart path)
Use FCM for receive/token andflutter_local_notifications to display notifications (including action buttons). Track opens with trackMetric and action presses with track('Push Notification Action Clicked', ...).
Token registration
Permission
Message listeners and tracking
push_notification_clicked. Use trackMetric(..., MetricEvent.opened) and the reserved action event name below.
Step 5: Identify Users
identify().
Local Notifications (Display + Action Buttons)
Channel IDzixflow_default, Android icon app_icon, iOS category ZX_2BTN, actions ACTION_0 / ACTION_1. Pass the full message.data map as JSON payload so taps can track and deep-link.
payload: jsonEncode(data) with the FCM data map so taps can track. Parse up to two AndroidNotificationActions (ACTION_0 / ACTION_1), download image_url / large_icon_url, map sticky to Android ongoing (with autoCancel: true), and set iOS categoryIdentifier: 'ZX_2BTN'. If template_id is present, route to a template-specific renderer first — see Template-Based Custom Rendering.
Background FCM handler
The background handler must also display the local notification (so action buttons work when the app is backgrounded or terminated):Push Payload Fields
Zixflow always puts these tracking keys indata:
Native: display content is in FCM
notification / APNs aps. data carries tracking + routing keys (deeplink_url, action_buttons, template_id, notif_id, workspace_id). Full wire format: Push Notification Tracking.
Custom: there is no notification block — the full content is in data. Official display/routing keys:
Example Custom
data:
iOS-Specific Setup
Push capabilities
- Open
ios/Runner.xcworkspace - Runner target → Signing & Capabilities
- Add Push Notifications
- Add Background Modes → enable Remote notifications
AppDelegate — register ZX_2BTN
Notification Service Extension (rich push + delivered)
- File → New → Target → Notification Service Extension (e.g.
NotificationServiceExtension) - Enable App Groups on Runner and the extension (same group ID, e.g.
group.com.yourcompany.app) - Podfile — NSE only (Runner does not need
zixflow/fcmfor this Dart path):
NotificationService.swift:
cdpApiKey (not apiKey) and set .appGroupId(...) to the same App Group as the host app.
Android-Specific Setup
Gradle (plugins DSL)
android/settings.gradle:
android/app/build.gradle:
Permissions and deeplink package visibility
app_icon (referenced by AndroidInitializationSettings('app_icon')).
Deeplink opening (example pattern)
Zixflow sendsdeeplink_url for body taps and action_buttons[].deeplink for action taps. After tracking Opened / Action Clicked, open those URLs. The sample app (push_handlers.dart + navigation.dart) uses:
navigatorKey to MaterialApp. Full open / action / delivered contract: Push Notification Tracking.
Sticky Notifications
data.sticky is an official Custom-mode field. Map it in your AndroidNotificationDetails. Native OS rendering does not honor sticky.
Values:
“Sticky” means the notification survives passive dismissal — the user swiping it away, or hitting “Clear all”. This is Android-only and only works on the Custom (app-rendered) path.
This requires two separate Android notification flags, not one — and a third piece of manual cleanup for action buttons.
autoCancel: truemust always be on, regardless ofstickyongoing: stickyis what actually blocks swipe and “Clear all” whenstickyis"until_click"/"true"/"ongoing""until_swipe"/"no_clear"usesFLAG_NO_CLEARwithoutongoing- Action buttons need an explicit cancel call in your action handler
Template-based custom rendering (template_id)
Every push sent from a dashboard template includes data.template_id. Map known IDs to a dedicated renderer (see push_templates.dart) and fall back to the generic field-driven builder. The sample app also checks template_type == "custom" as a local routing gate — the official payload field is template_id.
Full pattern: Template-Based Custom Rendering.
Testing
- Register token +
identify()a test user - Enable
LogLevel.debug - Send a test push from the Zixflow dashboard (add your custom-data keys if testing buttons or deeplinks)
- Use a physical iOS device; Android emulator needs Google Play Services
Troubleshooting
Best Practices
- Initialize Firebase before Zixflow, then your push handlers
- Identify users after login;
clearIdentify()on logout - Store the full FCM data map in the local-notification payload for tracking
- If you add per-button links, prefer the button link over the body link on action taps
- Cancel the notification ID on action press (Android)
- Re-initialize Zixflow in the background notification-response isolate
- See Push Notification Tracking for metrics details