You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Cloud Functions和Cloud Messaging实现Firestore新增文档的Flutter推送通知

问题解决与需求实现指南

一、先解决Cloud Functions部署与触发报错问题

1. 修复Admin SDK初始化代码

在Cloud Functions云端环境中,Firebase会自动为Admin SDK提供默认凭据,不需要手动加载serviceAccountKey.json,手动配置会导致部署冲突。修改函数代码如下:

import * as functions from "firebase-functions";
import * as admin from "firebase-admin";

// 直接初始化,无需额外参数
admin.initializeApp();

exports.pushNotifications = functions
    .region('europe-central2')
    .firestore.document("signals/{docId}").onCreate(async (snapshot) => {
        // 后续逻辑编写
    });

2. 解决权限拒绝问题

部署后出现access denied是因为Cloud Functions默认服务账号缺少必要权限,需在Firebase控制台IAM页面配置:

  • 找到项目默认服务账号:[项目ID]@appspot.gserviceaccount.com
  • 为其添加Firebase Cloud Messaging Admin角色(用于发送推送)
  • 同时确保该账号拥有Cloud Firestore Viewer角色(用于读取signals文档内容)

二、实现三个核心需求

1. 全状态通知显示(前台/后台/停止)

后台状态已正常,前台需手动监听消息并展示本地通知,先安装flutter_local_notifications包,补充Flutter端代码:

import 'package:flutter_local_notifications/flutter_local_notifications.dart';

final FlutterLocalNotificationsPlugin _localNotifications = FlutterLocalNotificationsPlugin();

// 初始化本地通知(app启动时调用)
Future<void> initLocalNotifications() async {
  const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
  const InitializationSettings initSettings = InitializationSettings(android: androidSettings);
  await _localNotifications.initialize(initSettings);
}

// 监听前台消息并展示通知
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  RemoteNotification? notification = message.notification;
  AndroidNotification? android = message.notification?.android;

  if (notification != null && android != null) {
    _localNotifications.show(
      notification.hashCode,
      notification.title,
      notification.body,
      NotificationDetails(
        android: AndroidNotificationDetails(
          'signal_notification_channel', // 自定义渠道ID
          'Signal Notifications', // 渠道名称
          importance: Importance.max,
          priority: Priority.high,
        ),
      ),
      payload: message.data['signalId'], // 传递信号ID
    );
  }
});

// 处理后台/前台通知点击跳转
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
  String signalId = message.data['signalId'] ?? '';
  // 跳转到信号详情页,示例:
  // Navigator.push(context, MaterialPageRoute(builder: (context) => SignalDetailPage(id: signalId)));
});

// 处理应用未启动时的通知点击
Future<void> handleInitialMessage() async {
  RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage();
  if (initialMessage != null) {
    String signalId = initialMessage.data['signalId'] ?? '';
    // 跳转到详情页
  }
}

调用顺序:app初始化时先执行initLocalNotifications()和handleInitialMessage(),再配置Firebase Messaging。

2. 定向推送(按区域发送)

第一步:存储用户订阅信息

在Firestore创建user_subscriptions集合,每个文档结构如下:

{
  "userId": "用户Firebase UID",
  "fcmToken": "用户设备FCM token",
  "watchedRegions": ["区域A", "区域B"] // 用户关注的区域
}

Flutter端在用户设置关注区域时更新该文档,注意定期更新token(token可能会变化)。

第二步:修改Cloud Functions逻辑

新signal创建时,读取对应区域的订阅token并批量发送:

exports.pushNotifications = functions
    .region('europe-central2')
    .firestore.document("signals/{docId}").onCreate(async (snapshot) => {
        const signalData = snapshot.data();
        const signalRegion = signalData.region; // 假设signal文档包含region字段
        const signalId = snapshot.id;

        // 查询关注该区域的用户token
        const subSnapshot = await admin.firestore()
            .collection('user_subscriptions')
            .where('watchedRegions', 'array-contains', signalRegion)
            .get();

        const tokens = subSnapshot.docs.map(doc => doc.data().fcmToken).filter(token => token != null);
        
        if (tokens.length === 0) {
            console.log('该区域无订阅用户');
            return null;
        }

        // 构造推送内容
        const payload = {
            notification: {
                title: '新信号通知',
                body: `区域${signalRegion}新增一条信号`
            },
            data: {
                signalId: signalId, // 传递信号ID
                region: signalRegion
            }
        };

        // 批量发送推送
        const response = await admin.messaging().sendToDevice(tokens, payload);
        console.log('推送发送完成', response);
        return response;
    });

3. 通知关联信号详情

上述代码已通过data字段传递signalId,Flutter端在onMessageOpenedApp和handleInitialMessage回调中获取该ID,直接跳转至对应信号详情页面即可。


内容的提问来源于stack exchange,提问作者Miro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 20:16:07