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

Flutter跨设备定时推送通知优化咨询:基于现有FCM即时推送代码

实现FCM定时推送的两种方案

FCM本身不提供直接的定时推送API,结合你现有的Flutter代码,以下是两种可行的实现方案,优先推荐云端方案保证可靠性。


一、可靠方案:云函数+Firebase数据库

通过后端定时触发推送请求,完全不受客户端状态限制,是生产环境的首选方案:

步骤1:在Flutter端新增定时任务存储方法

把用户设置的定时推送参数存入Firestore,供云函数扫描执行:

import 'package:cloud_firestore/cloud_firestore.dart';

class PushNotificationService {
  // ... 保留原有getAccessToken、sendPushNotification方法 ...

  // 新增:提交定时推送任务到Firestore
  static Future<void> schedulePushNotification(
    String deviceToken,
    String title,
    String body,
    String tripID,
    DateTime scheduledTime,
  ) async {
    await FirebaseFirestore.instance.collection('scheduled_pushes').add({
      'deviceToken': deviceToken,
      'title': title,
      'body': body,
      'tripID': tripID,
      'scheduledTime': Timestamp.fromDate(scheduledTime),
      'isSent': false, // 标记任务是否已执行
      'createdAt': Timestamp.now(),
    });
  }
}

步骤2:编写Firebase云函数定时执行推送

在Firebase控制台创建Node.js云函数,设置定时触发器(比如每分钟扫描一次),自动处理到达时间的推送任务:

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

const firestore = admin.firestore();
const messaging = admin.messaging();

// 每分钟触发一次的定时扫描函数
exports.sendScheduledPushes = functions.pubsub.schedule('* * * * *').onRun(async (context) => {
  const now = new Date();
  // 查询所有未发送且已到推送时间的任务
  const snapshot = await firestore.collection('scheduled_pushes')
    .where('isSent', '==', false)
    .where('scheduledTime', '<=', admin.firestore.Timestamp.fromDate(now))
    .get();

  const taskPromises = [];
  snapshot.forEach(doc => {
    const taskData = doc.data();
    // 构造FCM消息
    const message = {
      token: taskData.deviceToken,
      notification: {
        title: taskData.title,
        body: taskData.body,
      },
      data: {
        tripID: taskData.tripID,
      },
    };
    // 执行推送并标记任务已完成
    taskPromises.push(
      messaging.send(message)
        .then(() => doc.ref.update({isSent: true}))
        .catch(err => functions.logger.error('推送失败:', err))
    );
  });

  await Promise.all(taskPromises);
  functions.logger.log(`完成${taskPromises.length}个定时推送任务`);
  return null;
});

二、客户端本地定时(仅适合轻量场景)

如果不想依赖后端,可使用Flutter本地定时插件,但受系统后台限制(安卓Doze模式、iOS后台挂起),无法保证100%触发,适合短时间内的前台定时需求:

步骤1:添加依赖插件

在pubspec.yaml中引入必要的包:

dependencies:
  flutter_local_notifications: ^16.1.0
  workmanager: ^0.5.1 # 处理安卓后台任务

步骤2:修改PushNotificationService实现本地定时

import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:workmanager/workmanager.dart';
import 'dart:io';
import 'dart:convert';

class PushNotificationService {
  // ... 保留原有getAccessToken、sendPushNotification方法 ...

  static final FlutterLocalNotificationsPlugin _localNotifier =
      FlutterLocalNotificationsPlugin();

  // 初始化本地通知插件
  static Future<void> initLocalNotifications() async {
    const AndroidInitializationSettings androidInit =
        AndroidInitializationSettings('@mipmap/ic_launcher');
    const DarwinInitializationSettings iosInit = DarwinInitializationSettings();
    const InitializationSettings initSettings =
        InitializationSettings(android: androidInit, iOS: iosInit);
    await _localNotifier.initialize(initSettings);
  }

  // 本地定时推送
  static Future<void> scheduleLocalPush(
    String deviceToken,
    String title,
    String body,
    String tripID,
    DateTime scheduledTime,
  ) async {
    final int delayMs = scheduledTime.difference(DateTime.now()).inMilliseconds;
    if (delayMs <= 0) {
      await sendPushNotification(deviceToken, title, body, tripID);
      return;
    }

    if (Platform.isAndroid) {
      // 安卓用Workmanager处理后台定时
      await Workmanager().registerOneOffTask(
        'scheduled_push_$tripID',
        'executeScheduledPush',
        inputData: {
          'deviceToken': deviceToken,
          'title': title,
          'body': body,
          'tripID': tripID,
        },
        initialDelay: Duration(milliseconds: delayMs),
      );
    } else if (Platform.isIOS) {
      // iOS用本地通知的schedule方法
      const NotificationDetails notificationDetails = NotificationDetails(
        android: AndroidNotificationDetails(
          'scheduled_channel',
          '定时推送',
          channelDescription: '定时触发的推送通知',
          importance: Importance.max,
        ),
        iOS: DarwinNotificationDetails(),
      );
      await _localNotifier.schedule(
        tripID.hashCode,
        title,
        body,
        scheduledTime,
        notificationDetails,
      );
    }
  }

  // Workmanager回调:执行推送
  static void callbackDispatcher() {
    Workmanager().executeTask((task, inputData) async {
      if (task == 'executeScheduledPush') {
        await sendPushNotification(
          inputData!['deviceToken'],
          inputData['title'],
          inputData['body'],
          inputData['tripID'],
        );
      }
      return Future.value(true);
    });
  }
}

注意事项

  • 安卓需在AndroidManifest.xml中配置Workmanager的权限与服务
  • iOS需在Info.plist中添加UIBackgroundModes,包含remote-notification和fetch权限

关键说明

  1. 优先选择云方案:客户端定时受系统限制,可靠性无法保证;云函数方案完全脱离客户端状态,适合生产环境
  2. 你现有代码中的getAccessToken方法已经符合2024年6月FCM的HTTPS连接要求,无需额外修改
  3. 云方案中可通过isSent字段避免重复推送,也可在任务完成后删除Firestore文档

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 01:52:08