Firebase通知热重载后重复接收问题求助
解决Firebase通知热重载后重复弹窗问题
问题根源
每次热重载时,_handlePushNotifications函数会被重新执行,导致FirebaseMessaging.onMessage.listen被多次注册。Flutter热重载不会自动清除之前注册的监听器,因此每重载一次就会新增一个监听回调。收到通知时,所有已注册的回调都会触发,从而弹出多个重复弹窗。
解决方案
通过管理StreamSubscription对象,确保每次注册新监听器前先取消旧的订阅,避免重复监听。同时优化弹窗关闭逻辑,移除冗余代码。
修改后代码示例
// 全局/类成员变量:保存通知监听的订阅对象,用于后续取消订阅 StreamSubscription<RemoteMessage>? _messageSubscription; // 全局/类成员变量:弹窗全局Key,避免每次函数调用重新创建 final GlobalKey<State<StatefulWidget>> dialogKey = GlobalKey<State>(); void _handlePushNotifications(NotificationViewModel notificationViewModel) { debugPrint('handlePushN activated'); //test FirebaseMessaging messaging = FirebaseMessaging.instance; messaging.requestPermission( alert: true, announcement: false, badge: true, carPlay: false, criticalAlert: false, provisional: false, sound: true, ); messaging.getToken().then((token) async { debugPrint('Device Token: $token'); var storedToken = await TokenHandler.loadTokenNoException("device_token"); if (token != null && token != storedToken) { notificationViewModel.postDeviceToken(token); } }); messaging.onTokenRefresh.listen((token) async { var storedToken = await TokenHandler.loadTokenNoException("device_token"); if (token != storedToken) { notificationViewModel.postDeviceToken(token); } debugPrint('Token refreshed: $token'); }); // 取消之前的订阅,避免重复监听 _messageSubscription?.cancel(); // 重新注册通知监听 _messageSubscription = FirebaseMessaging.onMessage.listen((RemoteMessage message) { debugPrint("New message arrived: ${message.data.entries.first}"); String? msg = message.data['msg']; String? sum = message.data['sum']; if (msg != null && sum != null) { // 关闭已存在的弹窗 if (dialogKey.currentState != null && dialogKey.currentState!.mounted) { Navigator.of(dialogKey.currentContext!).pop(); } showDialog( context: navigatorKey.currentContext!, barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( key: dialogKey, title: Text(sum), content: SingleChildScrollView( child: ListBody( children: <Widget>[ Text(msg), ], ), ), actions: [ TextButton( onPressed: () { // 只需一行即可关闭弹窗,移除冗余的pop调用 Navigator.of(context).pop(); }, child: const Text('Dismiss'), ), ], ); }, ); } }); }
核心修改点
- 管理订阅生命周期:新增
StreamSubscription变量保存监听实例,每次执行函数时先取消旧订阅,再注册新订阅,彻底避免多监听器共存。 - 固定弹窗全局Key:将
dialogKey提升为全局/类成员变量,避免每次函数调用重新创建,保证弹窗状态的一致性。 - 简化弹窗关闭逻辑:移除重复的
Navigator.pop调用,利用弹窗上下文直接关闭即可。
额外优化建议
如果该逻辑是在StatefulWidget中实现,建议将监听注册放在initState,取消订阅放在dispose中,利用Flutter的生命周期自动管理:
class _YourWidgetState extends State<YourWidget> { StreamSubscription<RemoteMessage>? _messageSubscription; final GlobalKey<State<StatefulWidget>> dialogKey = GlobalKey<State>(); @override void initState() { super.initState(); _handlePushNotifications(yourNotificationViewModel); } @override void dispose() { _messageSubscription?.cancel(); super.dispose(); } // _handlePushNotifications函数逻辑同上 }
内容的提问来源于stack exchange,提问作者Milena Rode-Kotzé
相关产品推荐
相关产品推荐

