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

Flutter 弹出本地通知时Pusher Beams单事件无限触发问题

问题描述

项目中使用pusher beams实现服务端事件推送,接入flutter local notifications实现应用收到事件时弹出通知的功能,经排查确认问题与flutter local notifications相关。

复现代码

在initState生命周期中调用了initPusherBeams()方法,相关代码如下:

@override
void initState() {
  super.initState();

  _setAuthData().then((_) {
    if (_user?.id != null) initPusherBeams();
  });

  // notification related
  _notiInit();
  _requestPermissions();
  _configureDidReceiveLocalNotificationSubject();
  _configureSelectNotificationSubject();

  // ask for app rating
  WidgetsBinding.instance.addPostFrameCallback((_) => _ratingDialog());
}

initPusherBeams方法实现逻辑如下:

initPusherBeams() async {
  // Let's see our current interests
  await PusherBeams.instance.setDeviceInterests([
    // 'App.Models.User.${_user!.id}',
    'debug-new'
  ]);

  // This is not intented to use in web
  if (!kIsWeb) {
    await PusherBeams.instance.onMessageReceivedInTheForeground(_onMessageReceivedInTheForeground);
  }
}

void _onMessageReceivedInTheForeground(Map<Object?, Object?> data) {
  AndroidNotificationDetails androidPlatformChannelSpecifics = const AndroidNotificationDetails(
    'channel',
    'My App Name',
    channelDescription: 'New user registered',
    playSound: false,
    styleInformation: DefaultStyleInformation(true, true),
  );

  const IOSNotificationDetails iOSPlatformChannelSpecifics = IOSNotificationDetails(presentSound: false);

  NotificationDetails platformChannelSpecifics = NotificationDetails(
    android: androidPlatformChannelSpecifics,
    iOS: iOSPlatformChannelSpecifics,
  );

  log(json.encode(data));

  // flutterLocalNotificationsPlugin.show(
  //   0,
  //   'New user registered',
  //   data['body'].toString(),
  //   platformChannelSpecifics,
  //   payload: data['title'].toString(),
  // );
}
  • 注释掉flutterLocalNotificationsPlugin.show相关代码时,单条事件仅触发一次,运行符合预期
  • 取消注释通知显示代码后,单条事件无限循环触发,通知持续重复弹出,异常代码段如下:
flutterLocalNotificationsPlugin.show(
  0,
  'New user registered',
  data['body'].toString(),
  platformChannelSpecifics,
  payload: data['title'].toString(),
);
问题原因

死循环由两个问题共同导致:

  1. Pusher Beams的前台消息监听没有做来源过滤,本地通知弹出时触发的系统消息事件,会被插件误识别为新的Pusher远端推送,再次进入_onMessageReceivedInTheForeground回调
  2. 所有通知固定使用id=0,同ID通知触发的系统事件会反复投递,回调中再次调用show方法弹出通知,形成「收推送→弹通知→触发回调→再弹通知」的无限闭环
修复方案
  • 给消息回调加来源过滤:在_onMessageReceivedInTheForeground开头增加判断,仅处理结构匹配Pusher远端推送的消息,非Pusher来源的事件直接return打断循环,判断字段可根据实际推送结构调整,示例:
void _onMessageReceivedInTheForeground(Map<Object?, Object?> data) {
  // 过滤非Pusher推送的事件,避免本地通知触发误判
  if (!data.containsKey('pusher') && data['notification'] == null) {
    return;
  }
  // 原有通知配置逻辑保留
  AndroidNotificationDetails androidPlatformChannelSpecifics = const AndroidNotificationDetails(
    'channel',
    'My App Name',
    channelDescription: 'New user registered',
    playSound: false,
    styleInformation: DefaultStyleInformation(true, true),
  );

  const IOSNotificationDetails iOSPlatformChannelSpecifics = IOSNotificationDetails(presentSound: false);

  NotificationDetails platformChannelSpecifics = NotificationDetails(
    android: androidPlatformChannelSpecifics,
    iOS: iOSPlatformChannelSpecifics,
  );

  log(json.encode(data));

  flutterLocalNotificationsPlugin.show(
    // 替换固定ID为动态唯一值,避免同ID通知重复触发事件
    DateTime.now().millisecondsSinceEpoch ~/ 1000,
    'New user registered',
    data['body'].toString(),
    platformChannelSpecifics,
    payload: data['title'].toString(),
  );
}
  • 替换固定通知ID:不要给所有通知传id=0,使用消息自带的唯一ID、或者时间戳生成动态通知ID,避免同ID通知的系统事件重复投递
  • 校验过滤规则:可在回调中打印循环触发时的data内容,对比真实Pusher推送的消息结构,调整过滤条件做到100%匹配远端推送,彻底拦截误触发

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 09:46:01