Use when setting up Firebase Cloud Messaging, managing permissions and tokens, handling background/foreground notification taps, or dispatching messages server-side (HTTP v1).
npx skills add https://github.com/evanca/flutter-ai-rules --skill firebase-messaging
This skill defines how to correctly use Firebase Cloud Messaging (FCM) in Flutter applications.
Use this skill when:
flutter pub add firebase_messaging
iOS:
Android:
onCreate() and onResume().Web:
firebase-messaging-sw.js in your web/ directory:importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");
firebase.initializeApp({ /* your config */ });
const messaging = firebase.messaging();
messaging.onBackgroundMessage((message) => {
console.log("onBackgroundMessage", message);
});
Foreground messages:
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Foreground message data: ${message.data}');
if (message.notification != null) {
print('Notification: ${message.notification}');
}
});
Background messages:
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// Initialize Firebase before using other Firebase services in background
await Firebase.initializeApp();
print("Background message: ${message.messageId}");
}
void main() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
Background handler rules:
@pragma('vm:entry-point') (Flutter 3.3.0+) to prevent removal during tree shaking in release mode.Firebase.initializeApp() before using any other Firebase services.NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
alert: true,
badge: true,
sound: true,
announcement: false,
carPlay: false,
criticalAlert: false,
provisional: false,
);
print('Authorization status: ${settings.authorizationStatus}');
authorizationStatus returns authorized if the user has not disabled notifications in OS settings.provisional: true) to let users choose notification types after receiving their first notification.Get FCM registration token (use to send messages to a specific device):
final fcmToken = await FirebaseMessaging.instance.getToken();
Web — provide VAPID key:
final fcmToken = await FirebaseMessaging.instance.getToken(
vapidKey: "BKagOny0KF_2pCJQ3m....moL0ewzQ8rZu"
);
Listen for token refresh:
FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) {
// Send updated token to your application server
}).onError((err) {
// Handle error
});
Apple platforms — ensure APNS token is available before FCM calls:
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
// Safe to make FCM plugin API requests
}
Token Lifecycle (Auth State):
Tokens should be tied to user sessions. Save the token to your database when a user signs in, and delete the token (or remove it from the user's document) when they sign out. An FCM token is device-specific, not inherently tied to user auth data — failing to clear it on sign-out means the next user on that device might receive the previous user's notifications.
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
onMessage stream and manually display a visual cue (using your own UI logic or a local notifications plugin).meta-data to your <application> block in AndroidManifest.xml: <meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="high_importance_channel" />
Disable auto-init — iOS (Info.plist):
FirebaseMessagingAutoInitEnabled = NO
Disable auto-init — Android (AndroidManifest.xml):
<meta-data android:name="firebase_messaging_auto_init_enabled" android:value="false" />
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
Re-enable at runtime:
await FirebaseMessaging.instance.setAutoInitEnabled(true);
> Important: The iOS simulator does not display images in push notifications. Test on a physical device.
Messaging.serviceExtension().populateNotificationContent() in the extension for image handling.FirebaseMessaging Swift package to your extension target.Firebase/Messaging pod to your Podfile.When a user taps a notification, the app opens (or is brought to the foreground). Handle the interaction in both cases:
App was terminated:
RemoteMessage? initialMessage =
await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
// Navigate based on message content
}
App was in background:
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
// Navigate based on message content
});
Always handle both scenarios to ensure a smooth user experience regardless of app state when the notification was received.
// Subscribe
await FirebaseMessaging.instance.subscribeToTopic("weather_alerts");
// Unsubscribe
await FirebaseMessaging.instance.unsubscribeFromTopic("weather_alerts");
> Note: subscribeToTopic() and unsubscribeFromTopic() are not supported for web clients via the Flutter plugin.
The official Firebase documentation often obscures the exact steps for sending a test push notification. To fire a push (test or real) using the Firebase Console:
+ icon to add it.> To send real automated push notifications to production users, you must use a server implementation (via the FCM HTTP v1 API or the Firebase Admin SDK) rather than the console.
The legacy FCM server key endpoint was deprecated in June 2024 — HTTP v1 is the only supported option for sending pushes.
To authenticate server-to-server calls for HTTP v1, you need a Service Account:
.json file).FIREBASE_SERVICE_ACCOUNT) to your backend.To send an FCM HTTP v1 message, your backend must:
https://www.googleapis.com/auth/firebase.messaging, endpoint https://oauth2.googleapis.com/token).POST a JSON payload to https://fcm.googleapis.com/v1/projects/{project_id}/messages:send.Here is a minimal, complete working example using Node.js and the google-auth-library:
const { GoogleAuth } = require('google-auth-library');
// Read the securely-stored service account JSON from environment
const credentials = JSON.parse(process.env.FIREBASE_SERVICE_ACCOUNT);
async function getAccessToken() {
const auth = new GoogleAuth({
credentials,
scopes: ['https://www.googleapis.com/auth/firebase.messaging']
});
const client = await auth.getClient();
const token = await client.getAccessToken();
return token.token;
}
async function sendPushNotification(fcmToken, title, body) {
const accessToken = await getAccessToken();
const projectId = credentials.project_id;
const url = `https://fcm.googleapis.com/v1/projects/${projectId}/messages:send`;
const payload = {
message: {
token: fcmToken,
notification: {
title: title,
body: body,
},
// Target specific platform features (e.g., channel on Android, sound on iOS)
android: {
notification: {
channel_id: 'high_importance_channel',
}
},
apns: {
payload: {
aps: {
sound: 'default',
}
}
}
}
};
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
});
return response.json();
}
Analyzes meeting transcripts and recordings to uncover behavioral patterns, communication insights, and actionable feedback. Identifies when you avoid conflict, use filler words, dominate conversations, or miss opportunities to listen. Perfect for professionals seeking to improve their communication and leadership skills.
Toolkit for creating animated GIFs optimized for Slack, with validators for size constraints and composable animation primitives. This skill applies when users request animated GIFs or emoji animations for Slack from descriptions like "make me a GIF for Slack of X doing Y".
Analyzes your recent Claude Code chat history to identify coding patterns, development gaps, and areas for improvement, curates relevant learning resources from HackerNews, and automatically sends a personalized growth report to your Slack DMs.
Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack.
A skill that creates new Claude skills and automatically shares them on Slack using Rube for seamless team collaboration and skill discovery.
Automate Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify, etc.) using agent-browser via Chrome DevTools Protocol. Use when the user needs to interact with an Electron app, automate a desktop app, connect to a running app, control a native app, or test an Electron application. Triggers include "automate Slack app", "control VS Code", "interact with Discord app", "test this Electron app", "connect to desktop app", or any task requiring automation of a native Electron application.
Prepare meeting materials with Notion context and Codex research; use when gathering context, drafting agendas/pre-reads, and tailoring materials to attendees.
Interactive daily standup/meeting update generator. Use when user says 'daily', 'standup', 'scrum update', 'status update', 'what did I do yesterday', 'prepare for meeting', 'morning update', or 'team sync'. Pulls activity from GitHub, Jira, and Claude Code session history. Conducts 4-question interview (yesterday, today, blockers, discussion topics) and generates formatted Markdown update.
Take evanca/firebase-messaging from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.