Push notifications
optional, with your own Firebase project.
KESTREL sends push notifications through Firebase Cloud Messaging (HTTP v1). The app ships without any Firebase configuration, so it builds and runs with push off until you connect your own project.
01What you need
Push needs two things (docs/PUSH.md):
- your server holds a Firebase service account, so it can send;
- your app is built with your Firebase project's configuration, so the device can register.
Users then choose per notification group whether push is used (Notification settings).
Firebase Cloud Messaging is free. A project on the free Spark plan is enough for push.
021. Create a Firebase project
- Open the Firebase console (console.firebase.google.com) and create a project. Cloud Messaging is on by default.
- Add the apps you ship: Android (your application ID from
app/android/app/build.gradle.kts), iOS and macOS (your bundle ID), and web if you use the web app.
032. Server: the service account
- Create the key. Firebase console, Project settings, Service accounts, Generate new private key. A JSON file downloads.
- Give it to the server. Easiest, and the way that works with Docker: open the operator console, Integrations, paste the whole JSON into Firebase service account (JSON), save, and press Test. It answers "Signed in to project …" when Google accepts it.
- Or, without Docker: store the file on the server outside any web folder and outside Git, set
FCM_SERVICE_ACCOUNT_FILE=/path/to/service-account.jsonin.envand restart the server.
The Status page then shows Push (Firebase) as working, and the app's push option appears for users.
The service account JSON is a credential for your Firebase project. Never commit it, never put it in the app.
043. App: your Firebase configuration
From the app/ folder:
dart pub global activate flutterfire_cli
flutterfire configure --out=lib/push/firebase_options.dart
This overwrites the stub lib/push/firebase_options.dart with your project's values. Nothing else in the code changes. At start the app checks that file; with real values it starts Firebase after sign-in, asks for the notification permission and registers the device token with your server (app/lib/push/push_service.dart).
Do not commit your generated firebase_options.dart, google-services.json or GoogleService-Info.plist to a public repository. app/.gitignore already excludes google-services.json and GoogleService-Info.plist.
Android
Nothing more. On Android 13 and newer the app asks for the notification permission.
iOS and macOS
- Choose your Team in Xcode under Signing & Capabilities.
- Add the Push Notifications capability (and on iOS, Background Modes, Remote notifications).
- Upload your APNs authentication key in the Firebase console, Project settings, Cloud Messaging.
Web
- Create a Web Push key pair in the Firebase console, Project settings, Cloud Messaging, Web Push certificates, and put the public key into
kWebPushVapidKeyinlib/push/firebase_options.dart. Without it push stays off on the web. - Add
web/firebase-messaging-sw.js, a service worker that initialises Firebase with your web app's values (Firebase documentation: "Receive messages in a web app"). The page must be served over HTTPS.
05Check that it works
Sign in on a device and open Settings. "Push on this device" says one of these:
| Message | Meaning |
|---|---|
| Push notifications are on for this device | Configured and registered |
| Not available in this build: Firebase is not configured | The stub firebase_options.dart is still in place |
| Push is available, but notifications are blocked for this app | The user denied the permission; change it in the system settings |
| Push is configured, but this device could not register | Check the Firebase values, the APNs key (iOS, macOS) or the VAPID key (web) |
| Not available in the demo build | Demo builds never contact a server |
Then trigger a notification, for example a price alert that fires at once, and confirm the device receives it. Settings, Notification settings, Devices shows whether this device registered a push token and lets the user remove it.