Flutter按用户角色发送Firebase通知的报错解决方法
问题解决:Flutter中通过Firebase向指定角色用户发送通知
错误原因
FirebaseMessaging是客户端SDK,仅负责设备令牌管理、通知接收与处理,本身没有sendNotification方法。发送FCM通知必须通过服务器端API完成(避免在客户端暴露敏感密钥,保证安全性)。
实现方案
下面推荐两种可靠的实现方式,优先使用Firebase云函数(更安全):
方式一:使用Firebase Cloud Functions(推荐)
通过云函数查询目标用户令牌并调用FCM Admin SDK发送通知,全程在服务器端完成,无需暴露密钥。
1. 编写云函数(Node.js)
首先初始化Firebase Functions(若未配置,先安装firebase-tools并执行firebase init functions),然后修改functions/index.js:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); // 定义可被Flutter客户端调用的云函数 exports.sendReportNotification = functions.https.onCall(async (data, context) => { // 验证调用者身份(可选,确保只有登录用户能触发) if (!context.auth) { throw new functions.https.HttpsError("unauthenticated", "请先登录"); } // 从Firestore查询所有role为reviewer或admin的用户 const usersSnapshot = await admin.firestore() .collection("users") .where("role", "in", ["reviewer", "admin"]) .get(); const tokens = []; usersSnapshot.forEach(doc => { const token = doc.data().fcmToken; if (token) tokens.push(token); }); if (tokens.length === 0) { return { success: true, message: "无目标用户需要通知" }; } // 构造通知内容 const payload = { notification: { title: "新举报提醒", body: `${data.reportedBy}提交了一条举报,请及时处理`, clickAction: "FLUTTER_NOTIFICATION_CLICK" }, data: { reportId: data.reportId // 自定义数据,用于客户端跳转至举报详情 } }; // 批量发送通知 const response = await admin.messaging().sendMulticast({ tokens: tokens, notification: payload.notification, data: payload.data }); functions.logger.log(`成功发送${response.successCount}条,失败${response.failureCount}条`); return { success: true, successCount: response.successCount, failureCount: response.failureCount }; });
2. Flutter客户端调用云函数
先安装依赖:
flutter pub add cloud_functions firebase_auth
在Report按钮的点击事件中调用云函数:
import 'package:cloud_functions/cloud_functions.dart'; import 'package:firebase_auth/firebase_auth.dart'; void handleReportSubmit(String reportId) async { final currentUser = FirebaseAuth.instance.currentUser; if (currentUser == null) { // 处理未登录逻辑,比如提示用户登录 return; } try { final callable = FirebaseFunctions.instance.httpsCallable('sendReportNotification'); final result = await callable.call({ 'reportedBy': currentUser.displayName ?? currentUser.email, 'reportId': reportId, }); print('通知发送结果:${result.data}'); // 显示成功提示,比如SnackBar } catch (e) { print('发送失败:$e'); // 显示错误提示 } }
方式二:Flutter客户端直接调用FCM HTTP API(不推荐)
此方式需要暴露FCM服务器密钥,存在安全风险,仅用于测试场景。
1. 获取FCM服务器密钥
在Firebase控制台 → 项目设置 → 云消息传递 → 复制服务器密钥。
2. 编写发送代码
先安装依赖:
flutter pub add http cloud_firestore firebase_auth
实现发送逻辑:
import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; void sendNotificationToAdmins() async { // 从Firestore获取目标用户令牌 final usersSnapshot = await FirebaseFirestore.instance .collection('users') .where('role', 'in', ['reviewer', 'admin']) .get(); final tokens = usersSnapshot.docs .map((doc) => doc.data()['fcmToken']) .where((token) => token != null) .cast<String>() .toList(); if (tokens.isEmpty) return; const serverKey = '你的FCM服务器密钥'; const fcmUrl = 'https://fcm.googleapis.com/fcm/send'; final headers = { 'Content-Type': 'application/json', 'Authorization': 'key=$serverKey', }; final body = jsonEncode({ 'registration_ids': tokens, 'notification': { 'title': '新举报提醒', 'body': '有用户提交了举报,请处理', }, 'data': { 'reportId': 'report_123', }, }); final response = await http.post(Uri.parse(fcmUrl), headers: headers, body: body); if (response.statusCode == 200) { print('通知发送成功'); } else { print('发送失败:${response.body}'); } }
补充:客户端令牌保存
确保用户登录时,将FCM令牌保存到Firestore的用户文档中:
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:firebase_auth/firebase_auth.dart'; Future<void> _backgroundMessageHandler(RemoteMessage message) async { await Firebase.initializeApp(); print('后台收到通知:${message.notification?.title}'); } void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); // 配置后台通知处理 FirebaseMessaging.onBackgroundMessage(_backgroundMessageHandler); // 获取并保存FCM令牌 final token = await FirebaseMessaging.instance.getToken(); final user = FirebaseAuth.instance.currentUser; if (user != null && token != null) { await FirebaseFirestore.instance.collection('users').doc(user.uid).update({ 'fcmToken': token, }); } runApp(MyApp()); }
内容的提问来源于stack exchange,提问作者Yomal Amarathunge
相关产品推荐
相关产品推荐

