Docs / Firebase & Push Notifications
🏠 Home

Firebase & Push Notifications

How to enable real lock-screen push notifications in the mobile apps with Firebase Cloud Messaging β€” the Firebase project, the HTTP v1 service account in the admin panel, and the push template seeder.

Nothing breaks before this page is done Until Firebase is configured, both apps run normally with push silently disabled β€” the in-app Updates inbox keeps working over plain HTTP.

Part 1 β€” Configure the apps in Firebase

Create the Firebase project

Go to console.firebase.google.com and create a project (or reuse an existing one). One project can hold both apps.

Firebase console β€” the Create a project wizard
The "Create a project" wizard β€” give the project a name and continue.

Add the Android apps with the exact package names

In the Firebase console add an Android app per Flutter project. The package name must match the applicationId in each app's android/app/build.gradle.kts:

Package name: com.devtrixx.cargouser

Source: cargoproapp/android/app/build.gradle.kts (applicationId = "com.devtrixx.cargouser").

Package name: com.devtrixx.cargodriver

Source: cargoprodriver/android/app/build.gradle.kts (applicationId = "com.devtrixx.cargodriver").

Rebrand before you register Firebase The shipped Android ids are com.devtrixx.cargouser and com.devtrixx.cargodriver (iOS bundle ids match). If you publish under your own brand, change applicationId in android/app/build.gradle.kts and PRODUCT_BUNDLE_IDENTIFIER in ios/Runner.xcodeproj/project.pbxproj before adding the apps in Firebase β€” the package name is baked into the Firebase config and cannot be edited afterwards. Steps: Rename the application id / bundle id.
Firebase console β€” registering the Android app with its package name
Registering the Android app β€” enter the exact applicationId as the Android package name, then click Register app.

Download the generated google-services.json and put it in the app's android/app/ folder (e.g. cargoproapp/android/app/google-services.json).

Firebase console β€” downloading the google-services.json config file
Step 2 of registration β€” download google-services.json and place it in the app's android/app/ folder.

Add the iOS app

Add an iOS app with the bundle id you use in Xcode β€” the shipped defaults are com.devtrixx.cargouser (user app) and com.devtrixx.cargodriver (driver app) β€” and download GoogleService-Info.plist into ios/Runner/ of the app you are configuring. Open ios/Runner.xcworkspace once in Xcode and verify Runner β†’ Signing & Capabilities has Push Notifications (and Background Modes β†’ Remote notifications) β€” flutterfire configure usually adds these.

Run flutterfire configure in EACH app folder

dart pub global activate flutterfire_cli

# user app
cd cargoproapp
flutterfire configure

# driver app
cd ../cargoprodriver
flutterfire configure

The CLI regenerates lib/firebase_options.dart with real values and refreshes the native config files (android/app/google-services.json, ios/Runner/GoogleService-Info.plist).

User app: lib/firebase_options.dart ships as a placeholder β€” DefaultFirebaseOptions.isPlaceholder stays true and push stays disabled until this step runs.

Driver app: initializes Firebase from the native config files; its Gradle build applies the google-services plugin only when android/app/google-services.json exists, so the project still builds without one.

Part 2 β€” Connect the server

Download a Firebase service account key

In the Firebase console open Project settings β†’ Service accounts. Keep the default Firebase Admin SDK account selected and click Generate new private key. That downloads a JSON file with "type": "service_account", project_id, client_email and private_key.

Firebase console β€” Project settings, Service accounts tab with Generate new private key
Project settings β†’ Service accounts tab with Generate new private key.
Treat the JSON as a secret Anyone with that file can send pushes as your project. Store it only in the admin panel (or a private backup). Do not commit it to git or paste it into a public ticket.

Paste the JSON into the admin panel and enable FCM

In the Admin Panel go to Tools β†’ FCM settings: toggle FCM on and paste the full service account JSON. This writes fcm_active and fcm_service_account (fcm_sender_id is optional β€” HTTP v1 uses project_id from the JSON). From then on FcmService mints a short-lived OAuth token and POSTs each device token to https://fcm.googleapis.com/v1/projects/{project_id}/messages:send.

CargoPRO admin panel β€” Tools, FCM Configuration screen
The Tools β†’ FCM settings screen with the enable switch, service account JSON field and Test Connection button.

Seed the push templates

On the server, run the seeder that creates the localized push templates FcmService resolves for each event:

php artisan db:seed --class=PushTemplatesSeeder

It creates the notification_default passthrough (forwards the notification's own title/message) plus dedicated templates for package_registered, shipment_registered, pickup_created, status_updated, delivered, payment_confirmed, package_assigned, self_pickup_booked, invoice_uploaded and prealert_created β€” each in English, Arabic and Spanish, for the customer and admin roles. The seeder is idempotent (updateOrCreate), so re-running it is safe.

What the app does automatically

EventAction
Login / register / app start with a sessionFirebase init β†’ permission prompt β†’ token β†’ POST /api/device-tokens
Google rotates the tokenonTokenRefresh re-registers the new token automatically
Settings β†’ Push notifications toggled off, logout or session expirytoken removed via DELETE /api/device-tokens (best effort)
Push while the app is foregroundedshown as a local notification + inbox refresh
Push while backgrounded / terminatedshown by Android / iOS automatically

The driver app registers through the equivalent alias POST /api/notifications/device-token once the driver signs in.

Verify it works

Send a test push from the admin panel

Tools β†’ FCM has a Send test action (FcmService::sendTest) that pings the first active registered token.

Check that the device token row exists

Log in once on a device with the push toggle on, then:

php artisan tinker
> \App\Models\DeviceToken::count()

Trigger a real event and watch the log

Update a package status in the admin panel and tail storage/logs/laravel.log β€” you should see FCM send finished with the token counts.

Troubleshooting

SymptomCause / fix
PushNotificationService: Firebase not configured yetflutterfire configure was never run β€” lib/firebase_options.dart is still the placeholder
FCM service is not active, skipping in laravel.logTools β†’ FCM switch is off or the service account JSON is missing
No FCM tokens found for userthe app never registered β€” log in once on the device with the push toggle on
No push template foundseeder not run β€” run php artisan db:seed --class=PushTemplatesSeeder; notification_default covers unknown types once seeded
Pushes appear only inside the appnotification permission denied on the phone β€” check the OS notification settings
iOS pushes never arriveFCM requires a physical device; the iOS simulator does not receive pushes
Dead tokens clean themselves up When Google answers UNREGISTERED, NOT_FOUND or a matching invalid-token status, FcmService automatically deactivates that row in device_tokens, so stale installs stop counting as failures.