Dart开发:跨类传递Firebase Cloud Messaging消息Map始终为空的问题
问题根源
你现在遇到的核心问题是:在onMessage回调里创建了一个新的PushList(push: message)实例,但这个实例和当前页面上正在显示的PushList完全是两个独立的对象。Flutter的StatefulWidget状态是和它的Widget实例绑定的,你新创建的实例并没有被挂载到Widget树中,所以页面上的widget.push始终是初始的空值,不会更新。
解决方案:使用状态管理工具共享通知状态
最可靠的跨组件/跨类传递状态的方式是使用状态管理,这里推荐使用Flutter官方的provider包(简单易上手),步骤如下:
1. 添加provider依赖
在pubspec.yaml中添加:
dependencies: flutter: sdk: flutter provider: ^6.0.5 # 可替换为最新版本
2. 创建通知状态管理类
这个类负责存储当前收到的通知,并在通知更新时通知UI刷新:
import 'package:flutter/foundation.dart'; class NotificationProvider extends ChangeNotifier { Map<dynamic, dynamic>? _currentNotification; // 提供外部访问通知的方法 Map<dynamic, dynamic>? get currentNotification => _currentNotification; // 更新通知并通知UI void updateNotification(Map<dynamic, dynamic> message) { _currentNotification = message; notifyListeners(); // 触发所有监听该状态的UI组件刷新 } }
3. 在应用顶层注入状态管理器
在main.dart中用ChangeNotifierProvider包裹你的应用,让所有子组件都能访问到状态:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() { runApp( ChangeNotifierProvider( create: (context) => NotificationProvider(), child: const MyApp(), ), ); } // 你的MyApp类保持原有逻辑即可 class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Notification Demo', home: PushList(), // 假设PushList是首页 ); } }
4. 修改PushConfig类,更新状态
在PushConfig中获取状态管理器实例,收到通知时更新状态:
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class PushConfig { final BuildContext context; // 传入上下文,用于获取状态管理器 PushConfig(this.context); Future<void> pushAPIConfigMethod() async { FirebaseMessaging fcm = FirebaseMessaging.instance; // 监听前台收到的通知 FirebaseMessaging.onMessage.listen((RemoteMessage message) { print("onMessage: ${message.data}"); final title = message.notification?.title; final body = message.notification?.body; print("title: $title | body: $body"); // 更新通知状态 Provider.of<NotificationProvider>(context, listen: false) .updateNotification(message.data); }); // 处理应用从后台/冷启动打开的场景(可选) FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { print("onResume/onLaunch: ${message.data}"); Provider.of<NotificationProvider>(context, listen: false) .updateNotification(message.data); }); } }
注意:这里替换了旧的
fcm.configureAPI,改用Firebase Messaging最新的监听方式,旧API已被弃用,建议升级使用。
5. 修改PushList类,监听状态变化
让PushList监听NotificationProvider的状态变化,自动刷新UI:
import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class PushList extends StatefulWidget { const PushList({Key? key}) : super(key: key); @override _PushListState createState() => _PushListState(); } class _PushListState extends State<PushList> { @override void initState() { super.initState(); // 初始化推送配置 PushConfig(context).pushAPIConfigMethod(); } @override Widget build(BuildContext context) { // 监听通知状态变化,状态更新时自动重建UI final currentNotification = Provider.of<NotificationProvider>(context).currentNotification; return Scaffold( extendBody: true, backgroundColor: Colors.white, // 替换为你项目中的颜色变量 appBar: AppBar( title: const Text("Minhas Notificações"), ), body: ListView( children: [ currentNotification == null ? Card( elevation: 4, margin: const EdgeInsets.all(10), child: const Padding( padding: EdgeInsets.symmetric(vertical: 20, horizontal: 16), child: Text( "Você não possui novas notificações.", textAlign: TextAlign.center, ), ), ) : Card( elevation: 4, margin: const EdgeInsets.all(10), child: ListTile( title: Text(currentNotification['title'] ?? "Sem título"), subtitle: Text(currentNotification['body'] ?? "Sem conteúdo"), ), ) ], ), ); } }
其他可选方案
如果你的组件层级比较近(比如PushList是PushConfig的父组件),也可以用回调函数的方式传递通知:
- 在PushList中定义回调
void Function(Map<dynamic, dynamic>) onNotificationReceived - 将回调传递给PushConfig
- 在onMessage中调用回调,PushList收到后调用
setState更新push变量
但对于跨类/跨层级的场景,状态管理(比如Provider、Riverpod、Bloc)是更优雅、可扩展的方案。
内容的提问来源于stack exchange,提问作者Kaya Ryuuna
相关产品推荐
相关产品推荐

