firebase_fcm_client 0.0.2 copy "firebase_fcm_client: ^0.0.2" to clipboard
firebase_fcm_client: ^0.0.2 copied to clipboard

Firebase Cloud Messaging (FCM) HTTP v1 API client for Dart with secure service account impersonation. Send notifications to devices, topics, and conditions without managing private keys.

Firebase FCM Client #

Pub Version License: MIT

Firebase Cloud Messaging (FCM) HTTP v1 API client for Dart with secure service account impersonation.

Send push notifications to devices, topics, and conditions without managing private keys.

Features #

  • Send to Device Tokens - Target specific devices
  • Send to Topics - Broadcast to subscribed devices
  • Send to Conditions - Complex targeting with logical operators
  • Service Account Impersonation - Secure authentication, no private keys
  • Android Configuration - Customize Android-specific behavior
  • Web Push Configuration - Web-specific notification settings
  • Automatic Token Caching - Efficient token management
  • Automatic Token Refresh - Handles expiration transparently
  • Complete Error Handling - Detailed error messages
  • Full Type Safety - 100% Dart type-safe
  • Enterprise-Grade Security - No credentials in code
  • Production Ready - Battle-tested patterns

Installation #

Add to pubspec.yaml:

dependencies:
  firebase_fcm_client: ^0.0.1

Then run:

dart pub get

Setup #

1. Install Google Cloud SDK #

Download and install from: https://cloud.google.com/sdk/docs/install

2. Authenticate with Google Cloud #

gcloud auth application-default login

This opens a browser where you authenticate with your Google account and grants local credentials.

3. Get Service Account Email #

Ask your Google Cloud administrator for the FCM service account email:

fcm-sa@your-project.iam.gserviceaccount.com

Or find it yourself:

gcloud iam service-accounts list

4. Verify Permissions #

Ensure your account has the "Service Account Token Creator" role on the service account:

gcloud projects get-iam-policy YOUR_PROJECT \
  --flatten="bindings[].members" \
  --filter="bindings.members:YOUR_EMAIL@company.com"

Quick Start #

Basic Example #

import 'package:firebase_fcm_client/firebase_fcm_notification.dart';

void main() async {
  // Initialize with service account impersonation
  final auth = await ServiceAccountAuth.fromImpersonation(
    serviceAccountEmail: 'fcm-sa@your-project.iam.gserviceaccount.com',
    projectId: 'your-project-id',
  );

  final fcm = FCMClient(auth: auth, projectId: 'your-project-id');

  try {
    // Send notification to a device
    final result = await fcm.sendToToken(
      'device_token_here',
      title: 'Hello!',
      body: 'This is a test notification',
    );

    print('Message sent: $result');

    await auth.close();
  } catch (e) {
    print('Error: $e');
  }
}

API Reference #

ServiceAccountAuth #

Service account authentication using Google Cloud impersonation.

Constructor

final auth = await ServiceAccountAuth.fromImpersonation({
  required String serviceAccountEmail,
  required String projectId,
});

Parameters:

  • serviceAccountEmail: Service account email (e.g., fcm-sa@project.iam.gserviceaccount.com)
  • projectId: Google Cloud project ID

Throws:

  • Exception if Application Default Credentials not configured

Methods

// Get access token (cached automatically)
Future<String> getAccessToken()

// Close resources
Future<void> close()

FCMClient #

Firebase Cloud Messaging HTTP v1 API client.

Constructor

final fcm = FCMClient({
  required ServiceAccountAuth auth,
  required String projectId,
});

Send to Device Token

Send notification to a specific device:

final result = await fcm.sendToToken(
  'device_token',
  title: 'Title',
  body: 'Body text',
  data: {
    'key1': 'value1',
    'key2': 'value2',
  },
  android: AndroidConfig(
    priority: 'high',
    notification: AndroidNotification(
      color: '#FF5733',
      sound: 'default',
    ),
  ),
  webpush: WebpushConfig(
    notification: WebpushNotification(
      title: 'Web Title',
      icon: 'https://example.com/icon.png',
    ),
  ),
);

print('Sent: $result'); // Message ID

Parameters:

  • deviceToken (required): Device registration token
  • title (required): Notification title
  • body (required): Notification body text
  • data (optional): Custom data key-value pairs (max 4KB)
  • android (optional): Android-specific configuration
  • webpush (optional): Web push configuration

Returns: Message ID string

Throws: FCMException on error


Send to Topic

Send notification to all devices subscribed to a topic:

final result = await fcm.sendToTopic(
  'news',
  title: 'Breaking News',
  body: 'Important update available',
  data: {
    'story_id': '12345',
    'priority': 'high',
  },
);

print('Sent to topic: $result');

Parameters:

  • topic (required): Topic name
  • title (required): Notification title
  • body (required): Notification body text
  • data (optional): Custom data

Send to Condition

Send notification based on complex conditions:

final result = await fcm.sendToCondition(
  "'sports' in topics && 'cricket' in topics",
  title: 'Cricket News',
  body: 'Latest updates',
);

print('Sent to condition: $result');

Condition Examples:

// Single topic
"'topic1' in topics"

// Multiple topics (OR)
"'topic1' in topics || 'topic2' in topics"

// Multiple topics (AND)
"'topic1' in topics && 'topic2' in topics"

// Complex
"('sports' in topics && 'cricket' in topics) || 'breaking_news' in topics"

Models #

NotificationMessage

final message = NotificationMessage(
  token: 'device_token',  // OR topic/condition
  notification: NotificationPayload(
    title: 'Title',
    body: 'Body',
    imageUrl: 'https://example.com/image.png',
  ),
  data: {'key': 'value'},
  android: AndroidConfig(...),
  webpush: WebpushConfig(...),
);

AndroidConfig

final androidConfig = AndroidConfig(
  priority: 'high',  // 'high' or 'normal'
  notification: AndroidNotification(
    title: 'Custom Title',
    body: 'Custom Body',
    icon: 'ic_notification',
    color: '#FF5733',
    sound: 'default',
    priority: 1,
  ),
  collapseKey: 'message_group',
  ttl: 86400,  // Time to live in seconds
);

WebpushConfig

final webConfig = WebpushConfig(
  notification: WebpushNotification(
    title: 'Web Title',
    body: 'Web Body',
    icon: 'https://example.com/icon.png',
    badge: 'https://example.com/badge.png',
  ),
  data: {
    'url': 'https://example.com',
  },
);

Error Handling #

FCMException #

try {
  await fcm.sendToToken(token, title: '...', body: '...');
} on FCMException catch (e) {
  print('Error: ${e.message}');
  print('Code: ${e.code}');
  print('Status: ${e.statusCode}');
}

Common Errors #

Error Cause Solution
ADC not configured Missing gcloud auth application-default login Run: gcloud auth application-default login
Permission denied Missing IAM role Ask admin to grant "Service Account Token Creator"
Invalid service account Wrong email format Verify email: fcm-sa@project.iam.gserviceaccount.com
Invalid token Device token expired Get fresh token from Firebase Messaging
Topic not found Invalid topic name Ensure devices are subscribed to topic

Examples #

Send with Data Only (No Notification) #

await fcm.sendToToken(
  deviceToken,
  title: '',
  body: '',
  data: {
    'action': 'open_settings',
    'screen': 'profile',
  },
);

Send to Multiple Topics (Condition) #

await fcm.sendToCondition(
  "'notifications' in topics && 'offers' in topics",
  title: 'Special Offer',
  body: 'Limited time deal',
  data: {'offer_id': 'XYZ123'},
);

Send with Custom Android Configuration #

await fcm.sendToToken(
  deviceToken,
  title: 'Urgent Alert',
  body: 'Action required',
  android: AndroidConfig(
    priority: 'high',
    notification: AndroidNotification(
      color: '#FF0000',
      sound: 'alarm',
      priority: 2,
    ),
    collapseKey: 'alert',
    ttl: 3600,
  ),
);

Send with Web Push Configuration #

await fcm.sendToToken(
  deviceToken,
  title: 'Web Notification',
  body: 'Click to open',
  webpush: WebpushConfig(
    notification: WebpushNotification(
      title: 'Click Me',
      icon: 'https://example.com/icon.png',
      badge: 'https://example.com/badge.png',
    ),
    data: {
      'link': 'https://example.com/article',
    },
  ),
);

Send with Image #

await fcm.sendToToken(
  deviceToken,
  title: 'Check This Out',
  body: 'Image notification',
  data: {},
  android: AndroidConfig(
    notification: AndroidNotification(
      icon: 'ic_notification',
    ),
  ),
);

Best Practices #

1. Reuse ServiceAccountAuth #

// Create once
final auth = await ServiceAccountAuth.fromImpersonation(...);

// Use multiple times
final fcm1 = FCMClient(auth: auth, projectId: 'project1');
final fcm2 = FCMClient(auth: auth, projectId: 'project2');

// Close when done
await auth.close();

2. Implement Retry Logic #

Future<String> sendWithRetry(
  String token,
  String title,
  String body,
) async {
  int retries = 3;
  while (retries > 0) {
    try {
      return await fcm.sendToToken(token, title: title, body: body);
    } catch (e) {
      retries--;
      if (retries == 0) rethrow;
      await Future.delayed(Duration(seconds: 2));
    }
  }
  throw Exception('Failed after retries');
}

3. Validate Tokens Before Sending #

bool isValidToken(String token) {
  return token.isNotEmpty && token.length > 50;
}

if (isValidToken(deviceToken)) {
  await fcm.sendToToken(deviceToken, title: '...', body: '...');
}

4. Use Topics for Bulk Messaging #

// Instead of looping through tokens
for (final token in tokens) {
  await fcm.sendToToken(token, title: '...', body: '...');
}

// Use topics
await fcm.sendToTopic('users', title: '...', body: '...');

Testing #

Run unit tests:

dart test -r expanded

The package includes 24+ comprehensive tests covering:

  • Model serialization/deserialization
  • Message construction
  • Error handling
  • Complex message scenarios

Troubleshooting #

"ADC not configured" Error #

# Fix:
gcloud auth application-default login

"Permission denied" Error #

Ask your Google Cloud admin to run:

gcloud projects add-iam-policy-binding YOUR_PROJECT \
  --member=user:YOUR_EMAIL@company.com \
  --role=roles/iam.serviceAccountTokenCreator \
  --condition=resource.name==serviceAccounts/FCM_SERVICE_ACCOUNT

Package Not Found #

# Clear cache and reinstall
dart pub cache clean
dart pub get

Performance Notes #

  • Token Caching: Access tokens are cached and automatically refreshed
  • Network: Uses HTTP keep-alive for connection reuse
  • Concurrency: Multiple requests can run concurrently

Security #

  • No Private Keys: Uses service account impersonation
  • Automatic Token Refresh: Handles expiration transparently
  • Credential Isolation: Credentials never in code/config
  • Audit Trail: Google Cloud logs all access

Migration #

From Private Key Method #

// Old way (not recommended)
// final auth = ServiceAccountAuth.fromFile('key.json');

// New way (recommended)
final auth = await ServiceAccountAuth.fromImpersonation(
  serviceAccountEmail: 'fcm-sa@project.iam.gserviceaccount.com',
  projectId: 'your-project-id',
);

License #

MIT License - See LICENSE file for details

Support #

Contributing #

Contributions are welcome! Please feel free to submit a pull request.

Changelog #

See CHANGELOG.md for version history


Disclaimer #

This package is designed for server-side/backend use. Do NOT embed service account credentials in client applications.


Made with ❤️ for Flutter and Dart developers

0
likes
140
points
40
downloads

Publisher

unverified uploader

Weekly Downloads

Firebase Cloud Messaging (FCM) HTTP v1 API client for Dart with secure service account impersonation. Send notifications to devices, topics, and conditions without managing private keys.

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

ds_standard_features, googleapis_auth, json_annotation

More

Packages that depend on firebase_fcm_client