mcpField
F
firebase-messaging

firebase-messaging

github.com/evanca开发663205 次安装3 周前更新
代码仓库

关于 firebase-messaging

面向 Flutter 应用使用 Firebase Cloud Messaging 的指导技能,涵盖平台配置、消息处理、权限、令牌及通知行为。

主要功能

  1. 01按平台配置 FCM
  2. 02处理前台与后台消息
  3. 03管理权限与令牌
  4. 04处理通知交互和主题消息

适用场景

  1. 01为 Flutter 应用添加推送通知
  2. 02处理不同应用状态下的消息
  3. 03管理设备令牌和通知权限

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/evanca/flutter-ai-rules --skill firebase-messaging

安装后在 Claude Code 中输入/firebase-messaging确认已加载。

详细介绍

Firebase Cloud Messaging 技能

本技能介绍如何在 Flutter 应用中正确使用 Firebase Cloud Messaging (FCM)。

适用场景

在以下情况下使用本技能:

  • 在 Flutter 项目中设置 FCM 推送通知。
  • 处理应用处于前台、后台和终止状态时收到的消息。
  • 管理通知权限和 FCM 令牌。
  • 配置特定平台的通知显示行为。

1. 设置和配置

flutter pub add firebase_messaging

iOS:

  • 在 Xcode 中启用 Push Notifications 和 Background Modes。
  • 使用 FCM 前,先将 APNs 身份验证密钥上传到 Firebase。
  • 不要禁用方法混合(method swizzling)——FCM 令牌处理需要此功能。
  • 确保 APNs 身份验证密钥的 Bundle ID 与应用的 Bundle ID 相匹配。

Android:

  • 设备必须运行 Android 4.4+,并安装 Google Play 服务。
  • 在 onCreate() 和 onResume() 中检查 Google Play 服务兼容性。

Web:

  • 在 web/ 目录中创建并注册名为 firebase-messaging-sw.js 的 service worker 文件:
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);
});

2. 消息处理

前台消息:

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  print('Foreground message data: ${message.data}');
  if (message.notification != null) {
    print('Notification: ${message.notification}');
  }
});

后台消息:

@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());
}

后台处理程序规则:

  • 必须是顶层函数(不能是匿名函数,也不能是类方法)。
  • 使用 @pragma('vm:entry-point') 注解(Flutter 3.3.0+),以防函数在发布模式下被树摇优化移除。
  • 不能更新应用状态或执行会影响 UI 的逻辑——该处理程序在单独的 isolate 中运行。
  • 使用任何其他 Firebase 服务前,先调用 Firebase.initializeApp()。

3. 权限

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}');
  • iOS / macOS / Web / Android 13+: 接收 FCM 负载前必须请求权限。
  • Android < 13: 如果用户未在操作系统设置中关闭通知,authorizationStatus 会返回 authorized。
  • Android 13+: 在应用中跟踪权限请求——无法确定用户是否选择了授予或拒绝权限。
  • 在 iOS 上使用临时权限(provisional: true),让用户在收到第一条通知后选择通知类型。

4. 令牌管理

获取 FCM 注册令牌(用于向特定设备发送消息):

final fcmToken = await FirebaseMessaging.instance.getToken();

Web — 提供 VAPID 密钥:

final fcmToken = await FirebaseMessaging.instance.getToken(
  vapidKey: "BKagOny0KF_2pCJQ3m....moL0ewzQ8rZu"
);

监听令牌刷新:

FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) {
  // Send updated token to your application server
}).onError((err) {
  // Handle error
});

Apple 平台 — 调用 FCM 前确保 APNS 令牌可用:

final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
if (apnsToken != null) {
  // Safe to make FCM plugin API requests
}

令牌生命周期(身份验证状态): 令牌应与用户会话关联。用户登录时,将令牌保存到数据库;用户退出登录时,删除该令牌(或将其从用户文档中移除)。FCM 令牌是特定于设备的,本身并不与用户身份验证数据关联——如果退出登录时未清除令牌,该设备上的下一位用户可能会收到前一位用户的通知。


5. 特定平台的行为

  • iOS: 如果用户从应用切换器中划掉应用,则必须手动重新打开应用,后台消息才能再次正常工作。
  • Android: 如果用户在设备设置中强行停止应用,则必须手动重新打开应用。
  • iOS 前台通知: 更新显示选项,以便应用处于前台时显示通知:
    await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
      alert: true,
      badge: true,
      sound: true,
    );
    
  • Android 前台通知: 应用处于前台时收到的通知消息默认不会显示为可见通知。必须通过 onMessage 流接收负载,并手动显示视觉提示(使用自定义 UI 逻辑或本地通知插件)。
  • Android 默认渠道: 要为后台通知设置默认渠道,请将以下 meta-data 添加到 AndroidManifest.xml 中的 <application> 块:
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_channel_id"
        android:value="high_importance_channel" />
    

6. 自动初始化控制

禁用自动初始化 — iOS(Info.plist):

FirebaseMessagingAutoInitEnabled = NO

禁用自动初始化 — 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" />

在运行时重新启用:

await FirebaseMessaging.instance.setAutoInitEnabled(true);
  • 设置后,自动初始化设置会在应用重启后继续生效。

7. iOS 图片通知

**重要提示:**iOS 模拟器不会在推送通知中显示图片。请在实体设备上测试。

  • 在 Xcode 中添加 Notification Service Extension。
  • 在扩展中使用 Messaging.serviceExtension().populateNotificationContent() 处理图片。
  • Swift:将 FirebaseMessaging Swift 软件包添加到扩展目标。
  • Objective-C:将 Firebase/Messaging pod 添加到 Podfile。

8. 通知交互处理

用户轻点通知时,应用会打开(或切换到前台)。请在以下两种情况下处理交互:

应用已终止:

RemoteMessage? initialMessage =
    await FirebaseMessaging.instance.getInitialMessage();
if (initialMessage != null) {
  // Navigate based on message content
}

应用在后台运行:

FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
  // Navigate based on message content
});

请务必处理这两种情况,确保无论收到通知时应用处于什么状态,用户都能获得流畅的体验。


9. 主题消息

  • 订阅主题后,即可向多个选择加入的设备发送消息。
  • 主题消息最适合用于公开信息(例如天气更新),并针对吞吐量而非延迟进行优化。
// Subscribe
await FirebaseMessaging.instance.subscribeToTopic("weather_alerts");

// Unsubscribe
await FirebaseMessaging.instance.unsubscribeFromTopic("weather_alerts");

**注意:**Flutter 插件不支持 Web 客户端使用 subscribeToTopic() 和 unsubscribeFromTopic()。


10. 发送测试消息

Firebase 官方文档通常没有明确说明发送测试推送通知的具体步骤。要使用 Firebase 控制台发送推送通知(测试或正式),请按以下步骤操作:

  1. 获取设备的 FCM 注册令牌(参见第 4 节)。
  2. 前往 Firebase 控制台 并选择项目。
  3. 在左侧导航面板中找到 Engage(或 Run)部分,然后点击 Messaging(或 Cloud Messaging)。
  4. 点击 New campaign 并选择 Notifications。
  5. 输入 Notification title 和 Notification text。
  6. 点击 Send test message(通常是屏幕右侧的按钮)。
  7. 在对话框中输入 FCM 注册令牌,然后点击 + 图标将其添加。
  8. 确认已勾选令牌,然后点击 Test。

要向生产环境中的用户发送自动化正式推送通知,必须使用服务端实现(通过 FCM HTTP v1 API 或 Firebase Admin SDK),而不是使用控制台。


11. 服务端凭据与安全

旧版 FCM 服务器密钥端点已于 2024 年 6 月弃用——发送推送通知仅支持 HTTP v1。

要对 HTTP v1 服务端到服务端调用进行身份验证,需要服务账号:

  1. 前往 Firebase 控制台 → 项目设置 → 服务账号。
  2. 点击 Generate new private key(将下载一个 .json 文件)。
  3. **至关重要:**此文件包含高度敏感的机密信息,绝不能提交到 git。
  4. 安全存储该文件(例如存储在 Secret Manager 中),或将其字符串化内容作为环境变量(例如 FIREBASE_SERVICE_ACCOUNT)传递给后端。

12. 发送消息(HTTP v1)

要发送 FCM HTTP v1 消息,后端必须:

  1. 完成 OAuth2 JWT 交换(使用 RS256 签名,范围为 https://www.googleapis.com/auth/firebase.messaging,端点为 https://oauth2.googleapis.com/token)。
  2. 构造 JSON 负载,并将其 POST 到 https://fcm.googleapis.com/v1/projects/{project_id}/messages:send。

以下是使用 Node.js 和 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();
}

参考资料

评论

支持 Markdown