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(), );
问题原因
死循环由两个问题共同导致:
- Pusher Beams的前台消息监听没有做来源过滤,本地通知弹出时触发的系统消息事件,会被插件误识别为新的Pusher远端推送,再次进入
_onMessageReceivedInTheForeground回调 - 所有通知固定使用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
相关产品推荐
相关产品推荐

