Skip to main content

Overview

Trackmart uses Firebase Cloud Messaging (FCM) combined with Flutter Local Notifications to keep users informed about order status, driver location updates, and delivery notifications in real-time.
Push notifications require Firebase setup to be complete. Ensure you’ve followed the Firebase Setup guide before proceeding.

Why Push Notifications?

Notifications are critical for Trackmart’s user experience:

Order Updates

Notify buyers when drivers accept or complete orders

Driver Alerts

Alert drivers about new delivery requests

Real-time Updates

Keep users informed of delivery status changes

Chat Messages

Notify users of new messages from drivers or buyers

Dependencies

Trackmart uses two packages for notifications:
pubspec.yaml
dependencies:
  firebase_messaging: 5.1.2
  flutter_local_notifications: 0.8.0
  • firebase_messaging: Handles push notifications from Firebase Cloud Messaging
  • flutter_local_notifications: Displays notifications when app is in foreground

Step 1: Enable Firebase Cloud Messaging

1

Open Firebase Console

Navigate to your project in the Firebase Console.
2

Navigate to Cloud Messaging

Go to Project Settings > Cloud Messaging tab.
3

Note your Server Key

Copy the Server key - you’ll need this for sending notifications from your backend.
4

Enable Cloud Messaging API

Ensure the Cloud Messaging API is enabled in the Google Cloud Console.

Step 2: Configure Android

1

Update AndroidManifest.xml

Add to android/app/src/main/AndroidManifest.xml:
android/app/src/main/AndroidManifest.xml
<manifest>
  <application>
    <!-- FCM notification icon -->
    <meta-data
      android:name="com.google.firebase.messaging.default_notification_icon"
      android:resource="@drawable/notification_icon" />
    
    <!-- FCM notification color -->
    <meta-data
      android:name="com.google.firebase.messaging.default_notification_color"
      android:resource="@color/colorAccent" />
    
    <!-- FCM notification channel -->
    <meta-data
      android:name="com.google.firebase.messaging.default_notification_channel_id"
      android:value="trackmart_channel" />
  </application>
  
  <!-- Notification permissions -->
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.VIBRATE" />
  <uses-permission android:name="android.permission.WAKE_LOCK" />
</manifest>
2

Create notification icon

Add a notification icon to android/app/src/main/res/drawable/:
  • Create notification_icon.png (white icon on transparent background)
  • Recommended size: 24x24dp for different screen densities
3

Define notification color

Add to android/app/src/main/res/values/colors.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="colorAccent">#005B9A</color>
</resources>
Android 8.0 (API 26+) requires notification channels. Trackmart uses trackmart_channel as the default channel ID.

Step 3: Configure iOS

1

Enable Push Notifications capability

Open ios/Runner.xcworkspace in Xcode:
  1. Select Runner project
  2. Go to Signing & Capabilities tab
  3. Click + Capability
  4. Add Push Notifications
2

Enable Background Modes

In the same Signing & Capabilities tab:
  1. Add Background Modes capability
  2. Enable:
    • ☑️ Remote notifications
    • ☑️ Background fetch
3

Configure APNs Authentication

In the Firebase Console:
  1. Go to Project Settings > Cloud Messaging
  2. Under iOS app configuration, upload your APNs authentication key or certificate
  3. Follow the instructions to generate an APNs key from Apple Developer Portal
4

Update Info.plist

Add to ios/Runner/Info.plist if not already present:
<key>UIBackgroundModes</key>
<array>
  <string>fetch</string>
  <string>remote-notification</string>
</array>

Step 4: Initialize Firebase Messaging

Implement Firebase Messaging in your app’s main initialization:
lib/main.dart
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
  final FlutterLocalNotificationsPlugin _localNotifications = 
      FlutterLocalNotificationsPlugin();
  
  @override
  void initState() {
    super.initState();
    _configureNotifications();
  }
  
  void _configureNotifications() async {
    // Initialize local notifications
    const androidSettings = AndroidInitializationSettings('@drawable/notification_icon');
    const iosSettings = IOSInitializationSettings(
      requestSoundPermission: true,
      requestBadgePermission: true,
      requestAlertPermission: true,
    );
    
    const initSettings = InitializationSettings(
      android: androidSettings,
      iOS: iosSettings,
    );
    
    await _localNotifications.initialize(
      initSettings,
      onSelectNotification: _onNotificationTapped,
    );
    
    // Configure Firebase Messaging
    _firebaseMessaging.configure(
      onMessage: (Map<String, dynamic> message) async {
        print("onMessage: $message");
        _showLocalNotification(message);
      },
      onLaunch: (Map<String, dynamic> message) async {
        print("onLaunch: $message");
        _handleNotificationTap(message);
      },
      onResume: (Map<String, dynamic> message) async {
        print("onResume: $message");
        _handleNotificationTap(message);
      },
    );
    
    // Request iOS permissions
    _firebaseMessaging.requestNotificationPermissions(
      const IosNotificationSettings(
        sound: true,
        badge: true,
        alert: true,
      ),
    );
    
    // Get FCM token
    String token = await _firebaseMessaging.getToken();
    print("FCM Token: $token");
    
    // Save token to Firebase for this user
    _saveTokenToDatabase(token);
  }
}

Step 5: Handle Notifications

Implement notification handling based on app state:
When app is open and active:
void _showLocalNotification(Map<String, dynamic> message) async {
  // Extract notification data
  final notification = message['notification'];
  final data = message['data'];
  
  // Android notification details
  const androidDetails = AndroidNotificationDetails(
    'trackmart_channel',
    'Trackmart Notifications',
    'Notifications for order updates and messages',
    importance: Importance.high,
    priority: Priority.high,
    icon: '@drawable/notification_icon',
  );
  
  // iOS notification details
  const iosDetails = IOSNotificationDetails();
  
  // Combined platform details
  const platformDetails = NotificationDetails(
    android: androidDetails,
    iOS: iosDetails,
  );
  
  // Show notification
  await _localNotifications.show(
    notification.hashCode,
    notification['title'],
    notification['body'],
    platformDetails,
    payload: json.encode(data),
  );
}

Step 6: Save FCM Token

Store the device FCM token in Firebase for targeting notifications:
void _saveTokenToDatabase(String token) async {
  // Get current user ID
  final userId = await SharedPreferences.getInstance()
      .then((prefs) => prefs.getString('id'));
  
  if (userId != null) {
    // Save to Realtime Database
    await FirebaseDatabase.instance
        .reference()
        .child('users')
        .child(userId)
        .update({
      'fcmToken': token,
      'platform': Platform.isIOS ? 'ios' : 'android',
      'tokenUpdatedAt': ServerValue.timestamp,
    });
    
    // Also save to Firestore for querying
    await Firestore.instance
        .collection('users')
        .document(userId)
        .updateData({
      'fcmToken': token,
      'platform': Platform.isIOS ? 'ios' : 'android',
    });
  }
}
Tokens should be refreshed when they change. Listen to onTokenRefresh to update the database.

Step 7: Send Notifications

Notifications can be sent from:
Manual notifications for testing:
1

Open Cloud Messaging

Go to Firebase Console > Cloud Messaging > Send your first message
2

Compose notification

  • Notification title: “Order Accepted”
  • Notification text: “Your driver is on the way!”
3

Target users

Choose:
  • User segment: All users
  • Topic: Specific topic subscribers
  • Single device: Test with your FCM token
4

Send notification

Click Review and Publish to send.

Notification Types

Trackmart supports various notification types:
// Order accepted
{
  "notification": {
    "title": "Order Accepted",
    "body": "Your driver is on the way!"
  },
  "data": {
    "type": "order_update",
    "orderId": "O:1234567890",
    "status": "accepted",
    "driverId": "driver123"
  }
}

// Order in transit
{
  "notification": {
    "title": "Order In Transit",
    "body": "Your order is being delivered"
  },
  "data": {
    "type": "order_update",
    "orderId": "O:1234567890",
    "status": "in_transit"
  }
}

// Order delivered
{
  "notification": {
    "title": "Order Delivered",
    "body": "Your order has been delivered successfully"
  },
  "data": {
    "type": "order_update",
    "orderId": "O:1234567890",
    "status": "delivered"
  }
}
// New delivery request
{
  "notification": {
    "title": "New Delivery Request",
    "body": "You have a new delivery request nearby"
  },
  "data": {
    "type": "new_request",
    "orderId": "O:1234567890",
    "buyerId": "buyer123",
    "distance": "2.5km"
  }
}
// New message
{
  "notification": {
    "title": "Message from John",
    "body": "When will you arrive?"
  },
  "data": {
    "type": "new_message",
    "chatId": "chat123",
    "senderId": "user456",
    "senderName": "John"
  }
}

Testing Notifications

1

Get FCM token

Run the app and copy the FCM token printed in the console:
FCM Token: fL8X9...
2

Send test notification

Use the Firebase Console or a tool like Postman:
cURL
curl -X POST https://fcm.googleapis.com/fcm/send \
  -H "Authorization: key=YOUR_SERVER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "YOUR_FCM_TOKEN",
    "notification": {
      "title": "Test Notification",
      "body": "This is a test from Trackmart"
    },
    "data": {
      "type": "test"
    }
  }'
3

Verify delivery

Check that:
  • Notification appears when app is in foreground
  • System tray shows notification when app is backgrounded
  • Tapping notification opens the app correctly

Notification Channels (Android 8+)

Create notification channels for better user control:
Future<void> createNotificationChannels() async {
  final android = _localNotifications.resolvePlatformSpecificImplementation<
      AndroidFlutterLocalNotificationsPlugin>();
  
  if (android != null) {
    // Orders channel
    await android.createNotificationChannel(
      const AndroidNotificationChannel(
        'trackmart_orders',
        'Order Updates',
        'Notifications about your order status',
        importance: Importance.high,
      ),
    );
    
    // Messages channel
    await android.createNotificationChannel(
      const AndroidNotificationChannel(
        'trackmart_messages',
        'Messages',
        'New messages from drivers and buyers',
        importance: Importance.high,
      ),
    );
    
    // General channel
    await android.createNotificationChannel(
      const AndroidNotificationChannel(
        'trackmart_general',
        'General',
        'General app notifications',
        importance: Importance.defaultImportance,
      ),
    );
  }
}

Troubleshooting

  • Verify FCM is enabled in Firebase Console
  • Check that the app has notification permissions
  • Ensure FCM token is saved correctly in database
  • Verify APNs certificate/key is uploaded (iOS)
  • Check device internet connectivity
  • Ensure Push Notifications capability is enabled in Xcode
  • Verify APNs authentication key is uploaded to Firebase
  • Check that requestNotificationPermissions() is called
  • Test on a physical device (simulator has limited support)
  • Verify bundle ID matches in Firebase and Xcode
  • Ensure flutter_local_notifications is configured
  • Check that _showLocalNotification() is called in onMessage
  • Verify notification channel is created (Android)
  • Check notification permissions in app settings
  • Ensure onSelectNotification callback is set
  • Verify payload data is properly JSON encoded
  • Check navigation routes are registered
  • Test with different app states (foreground, background, terminated)

Best Practices

Token Management

  • Refresh tokens when they change
  • Store tokens with user profiles
  • Handle token errors gracefully
  • Clean up tokens on logout

User Experience

  • Keep notification text concise
  • Use meaningful titles
  • Include action buttons when relevant
  • Don’t spam users with notifications

Data Payload

  • Include navigation data in payload
  • Keep data payload small (under 4KB)
  • Use data-only messages for background processing
  • Include timestamps for time-sensitive data

Testing

  • Test all app states (foreground, background, terminated)
  • Verify on both iOS and Android
  • Test notification permissions flow
  • Monitor delivery rates in Firebase Console

Next Steps

Firebase Setup

Review Firebase configuration for notifications

Mapbox Integration

Add location-based notification triggers

Build docs developers (and LLMs) love