如何在Flutter代码中通过Firebase Messaging实现定时通知?
问题描述
我已经在Flutter项目中集成了Firebase推送通知功能,输入标题、描述并选择日期后点击按钮,Firebase会立即向设备发送通知。但我希望能根据应用内设置的具体日期和时间接收定时通知。
相关截图
- 应用UI:

- 通知截图:

UI代码
DateTime now = new DateTime.now(); void showDatePickerNextVaccinationDate() { DateTime mindate = DateTime(now.year , now.month, now.day); DateTime maxdate = DateTime(now.year + 12, now.month, now.day); showCupertinoModalPopup( context: context, builder: (BuildContext builder) { return Container( height: MediaQuery.of(context).copyWith().size.height * 0.25, color: Colors.white, child: CupertinoDatePicker( maximumYear: now.year + 12, minimumYear: now.year , mode: CupertinoDatePickerMode.dateAndTime, initialDateTime: mindate, onDateTimeChanged: (value) { if (value != nextDate) { setState(() { nextDate = value; //calAge(); }); } }, maximumDate: maxdate, minimumDate: mindate, ), ); }); } @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ TextFormField( controller: title, ), TextFormField( controller: body, ), GestureDetector( onTap: showDatePickerNextVaccinationDate, child: SizedBox( width: 500, height: 70, child: Container( decoration: BoxDecoration( border: Border.all(color: Colors.black), borderRadius: BorderRadius.circular(15), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: EdgeInsets.only( left: 3, top:20, bottom: 10, ), child: Text( nextDate == null ? 'Next Vaccinated Date' : DateFormat('HH MMMM dd yyyy ') .format(nextDate!), style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w300, ), ), ), Icon( Icons.arrow_drop_down, color: Colors.grey[700], size: 30.0, ), ], ), ), ), ), GestureDetector( onTap: () async { String name = "User1"; String titleText = title.text; String bodyText = body.text; String selectedNextDate = nextDate.toString(); if (name != "") { DocumentSnapshot snap = await FirebaseFirestore.instance .collection("UserTokens") .doc(name) .get(); String token = snap['token']; print(token); //call sendPushMessage sendPushMessage(token, titleText, bodyText,selectedNextDate); } }, child: Container( margin: EdgeInsets.all(20), height: 40, width: 200, decoration: BoxDecoration( color: Colors.red, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: Colors.redAccent.withOpacity(0.5), ) ]), child: Center( child: Text("button"), ), ), ) ], ), ), ); }
发送通知函数代码
void sendPushMessage(String token, String body, String title,String selectedNextDate) async { try { await http.post(Uri.parse('https://fcm.googleapis.com/fcm/send'), headers: <String, String>{ 'Content-Type': 'application/json', 'Authorization': 'key=....', }, body: jsonEncode<String, dynamic>{ 'priority': 'high', //want this to when click message open that page (payload function) 'data': <String, dynamic>{ 'click_action': 'Test_Notification', 'status': 'done', 'body': '$body $selectedNextDate', 'title': title, "isScheduled" : "true", "scheduledTime" : "2023-05-17 18:44:00" }, "notification": <String, dynamic>{ "title": title, "body":'$body $selectedNextDate', "android_channel_id": "dbfood", "isScheduled" : "true", "scheduledTime" : "2023-05-17 18:44:00" }, "to": token, }); } catch (e) { if (kDebugMode) { print("Error push notification"); } } }
解决方案
Firebase Cloud Messaging(FCM)本身不支持直接发送定时推送,要实现定时通知,有两种可行方案:
方案一:Firebase Cloud Functions + Cloud Scheduler(生产级推荐)
这是服务器端调度方案,不受应用状态影响,即使应用被杀死也能按时触发:
- 存储定时任务:用户设置好通知时间后,将通知标题、内容、目标token、触发时间等信息存入Firebase Firestore。
- 部署Cloud Function:编写云函数,读取Firestore中到期且未发送的任务,调用FCM API发送通知,同时标记任务为已完成或删除。
- 配置Cloud Scheduler:设置定时触发器(比如每分钟执行一次),调用云函数检查并处理到期任务。
示例云函数(JavaScript):
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); exports.sendScheduledNotifications = functions.pubsub.schedule('every 1 minutes').onRun(async (context) => { const now = new Date(); // 查询所有到期且未发送的任务 const snapshot = await admin.firestore().collection('scheduledNotifications') .where('scheduledTime', '<=', now) .where('sent', '==', false) .get(); const promises = []; snapshot.forEach(doc => { const data = doc.data(); // 构造FCM消息 const message = { token: data.token, notification: { title: data.title, body: data.body }, data: data.data }; // 发送通知 promises.push(admin.messaging().send(message)); // 标记任务已发送 promises.push(doc.ref.update({sent: true})); }); return Promise.all(promises); });
方案二:本地定时通知(仅适用于应用后台存活场景)
如果能接受应用被杀死后任务失效的限制,可以用flutter_local_notifications插件实现本地定时:
- 添加依赖:在
pubspec.yaml中加入flutter_local_notifications和shared_preferences。 - 初始化本地通知插件:
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin(); Future<void> initLocalNotifications() async { const AndroidInitializationSettings initializationSettingsAndroid = AndroidInitializationSettings('@mipmap/ic_launcher'); final InitializationSettings initializationSettings = InitializationSettings(android: initializationSettingsAndroid); await flutterLocalNotificationsPlugin.initialize(initializationSettings); }
- 调度本地通知:
void scheduleLocalNotification(DateTime scheduledTime, String title, String body) async { const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( 'dbfood', '疫苗提醒', channelDescription: '定时疫苗通知', importance: Importance.max, priority: Priority.high, ); const NotificationDetails notificationDetails = NotificationDetails(android: androidDetails); // 计算延迟时间(需确保目标时间晚于当前时间) int delay = scheduledTime.difference(DateTime.now()).inMilliseconds; if (delay > 0) { await flutterLocalNotificationsPlugin.schedule( 0, // 通知ID title, body, scheduledTime, notificationDetails, androidAllowWhileIdle: true, // 允许设备休眠时触发 ); } }
- 修改按钮点击逻辑:
GestureDetector( onTap: () async { String titleText = title.text; String bodyText = body.text; if (nextDate != null && titleText.isNotEmpty && bodyText.isNotEmpty) { // 调用本地通知调度方法 await scheduleLocalNotification(nextDate!, titleText, bodyText); } }, // ... 按钮UI代码 )
注意事项
- 服务器端方案稳定性更高,是生产环境首选;本地方案依赖应用后台存活能力,Android需处理电池优化限制,iOS定时机制有较多限制。
- 现有代码中硬编码的
scheduledTime需替换为用户选择的nextDate转换后的标准格式(如ISO8601)。
内容的提问来源于stack exchange,提问作者Dasun Dola
相关产品推荐
相关产品推荐

