Flutter中如何通过FCM HTTP v1 API从设备端编写通知(替代控制台)
解决方案总览
首先明确核心原则:FCM HTTP v1 API的服务账号密钥绝对不能放在Flutter客户端,一旦暴露,任何人都可以冒用你的权限发送通知。必须通过后端服务或Firebase Cloud Functions中转调用,这是安全底线。下面分模块解决你的问题:
一、安全实现FCM推送(避免密钥暴露)
1. 用Firebase Cloud Functions作为中转(推荐,无需自建后端)
编写Callable Function,客户端调用这个函数,由函数安全调用FCM v1 API发送通知——Firebase环境会自动管理服务账号密钥,无需手动存储json文件。
示例Cloud Functions代码(Node.js):
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.sendScheduledNotification = functions.https.onCall(async (data, context) => { // 可选:验证用户身份,确保只有合法用户调用 if (!context.auth) { throw new functions.https.HttpsError("unauthenticated", "用户未登录"); } const { token, title, body, scheduledTime } = data; // 校验定时时间合法性 const delay = scheduledTime - Date.now(); if (delay <= 0) { throw new functions.https.HttpsError("invalid-argument", "定时时间必须在未来"); } // 延迟执行推送(自动使用FCM v1 API) setTimeout(async () => { await admin.messaging().send({ token: token, notification: { title: title, body: body, }, data: { // 自定义数据,用于后续点击通知的逻辑跳转 type: "weather_alert", content: "明日降温提醒" } }); }, delay); return { success: true, message: "定时任务已创建" }; });
2. 自建后端服务
如果已有自己的后端,直接在后端集成Firebase Admin SDK,客户端调用后端接口,由后端处理FCM推送逻辑,同样避免密钥暴露在客户端。
二、Flutter客户端处理流程
1. 获取设备推送令牌(FCM Token)
使用firebase_messaging插件获取令牌,传给后端/Cloud Functions作为推送目标:
import 'package:firebase_messaging/firebase_messaging.dart'; Future<String?> getFCMToken() async { String? token = await FirebaseMessaging.instance.getToken(); return token; }
2. 处理通知点击事件
结合firebase_messaging和flutter_local_notifications插件,覆盖前台、后台、应用未启动三种场景的点击处理:
步骤1:初始化本地通知插件
import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'dart:convert'; final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); Future<void> initNotifications() async { const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); const InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid); // 初始化时绑定点击回调 await flutterLocalNotificationsPlugin.initialize(initializationSettings, onDidReceiveNotificationResponse: (NotificationResponse response) { handleNotificationClick(response.payload); }); }
步骤2:监听FCM消息并处理点击
// 前台收到消息时,显示本地通知 FirebaseMessaging.onMessage.listen((RemoteMessage message) { RemoteNotification? notification = message.notification; AndroidNotification? android = message.notification?.android; if (notification != null && android != null) { flutterLocalNotificationsPlugin.show( notification.hashCode, notification.title, notification.body, NotificationDetails( android: AndroidNotificationDetails( 'weather_channel', '天气提醒', channelDescription: '接收天气相关推送通知', icon: android.smallIcon, ), ), payload: jsonEncode(message.data), // 传递自定义数据到点击回调 ); } }); // 后台点击通知时触发 FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { handleNotificationClick(jsonEncode(message.data)); }); // 应用未启动时点击通知触发 Future<void> setupInitialMessageHandler() async { RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { handleNotificationClick(jsonEncode(initialMessage.data)); } } // 统一处理点击逻辑 void handleNotificationClick(String? payload) { if (payload == null) return; Map<String, dynamic> data = jsonDecode(payload); String type = data['type'] ?? ''; if (type == 'weather_alert') { // 跳转到天气提醒页面 // Navigator.push(context, MaterialPageRoute(builder: (context) => WeatherAlertPage())); } }
三、实现定时推送
方式1:服务端定时(推荐,稳定可靠)
- 单次定时:如Cloud Functions示例,客户端传递定时时间,服务端计算延迟后执行推送。
- 周期性定时:用Cloud Functions的
pubsub.schedule触发器实现固定时间推送(比如每天早8点):
exports.dailyWeatherAlert = functions.pubsub.schedule('0 8 * * *') // 每天早上8点 .timeZone('Asia/Shanghai') .onRun(async (context) => { // 从Firestore读取需要推送的用户token列表 const usersSnapshot = await admin.firestore().collection('users').get(); const tokens = usersSnapshot.docs.map(doc => doc.data().fcmToken).filter(token => token != null); await admin.messaging().sendEachForMulticast({ tokens: tokens, notification: { title: '今日天气提醒', body: '今日气温20-28℃,注意防晒' }, data: { type: 'weather_daily' } }); return null; });
方式2:本地定时(仅临时场景)
用flutter_local_notifications的本地定时通知,但应用被杀死后会失效,仅适合短时间内的定时提醒,不适合天气类长期推送需求。
关键注意事项
- 绝对禁止客户端存储服务账号密钥:FCM v1 API的密钥是服务器端专属凭证,客户端存储会导致泄露风险。
- 用户身份验证:在Cloud Functions或后端接口中验证用户身份(比如Firebase Auth的ID Token),避免恶意调用。
- Android通知渠道配置:Android 8.0+必须配置通知渠道,否则通知无法正常显示,示例中已包含相关配置。
内容的提问来源于stack exchange,提问作者WasimSafdar
相关产品推荐
相关产品推荐

