Flutter中Widget变量始终为null:FCM推送赋值后读取异常如何解决
问题根因
- 违反Flutter核心设计规则:StatefulWidget类的成员是不可变的,不能在异步回调里直接给Widget类的成员赋值。Widget是临时对象,每次UI重建都会生成新的Widget实例,你之前给旧实例赋值的内容根本不会被当前build流程读取到,自然拿到的是初始的null值。
- 变量名拼写错误:代码中定义的通知存储变量是
notific,赋值时却写为notificacao,赋值操作根本没有落到实际定义的成员变量上。 - 状态更新逻辑缺失:FCM消息接收是异步流回调,回调里修改数据后没有触发State层的重建流程,UI始终渲染初始的null值。
- 初始化时机错误:FCM监听方法没有绑定State的生命周期,既没有在页面初始化阶段正确触发,也没有在页面销毁时取消监听,存在监听不触发、内存泄漏的问题。
修正方案
把可变状态全部迁移到State类中,在State的生命周期内初始化FCM监听,用setState包裹所有状态更新逻辑,补全拼写错误,在页面销毁时取消流订阅:
class PushList extends StatefulWidget { const PushList({super.key}); @override _PushListState createState() => _PushListState(); } class _PushListState extends State<PushList> { // 将可变状态全部定义在State层,不放在不可变的Widget类中 Map<dynamic, dynamic>? _notific; String? _title; String? _body; // 定义流订阅对象,用于页面销毁时取消监听 StreamSubscription<RemoteMessage>? _messageSubscription; StreamSubscription<RemoteMessage>? _messageOpenedSubscription; @override void initState() { super.initState(); _initFCMReceiver(); } Future<void> _initFCMReceiver() async { // 处理应用被杀死状态下,点击通知打开应用的场景 final initialMessage = await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { _updateNotificationState(initialMessage); } // 处理应用在前台运行时收到推送的场景 _messageSubscription = FirebaseMessaging.onMessage.listen((RemoteMessage message) { _updateNotificationState(message); }); // 处理应用在后台运行时,点击通知打开应用的场景 _messageOpenedSubscription = FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) { _updateNotificationState(message); }); } // 统一处理通知状态更新,用setState包裹触发UI重建 void _updateNotificationState(RemoteMessage message) { if (message.notification != null) { setState(() { _notific = { 'title': message.notification!.title, 'body': message.notification!.body }; _title = message.notification!.title; _body = message.notification!.body; }); print('收到推送消息: $_notific'); } } @override void dispose() { // 页面销毁时取消所有流订阅,防止内存泄漏 _messageSubscription?.cancel(); _messageOpenedSubscription?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( extendBody: true, backgroundColor: Colors.white, body: ListView( children: [ _notific != null ? Card( margin: const EdgeInsets.all(10), elevation: 4, child: ListTile( title: Text(_title ?? ''), subtitle: Text(_body ?? ''), ), ) : const Center( child: Text("U don't have new notifcs."), ), ], ), ); } }
注意事项
- 所有和UI展示绑定的可变状态,必须定义在State类中,不要放在不可变的Widget类里
- 所有异步回调中的状态修改,必须用
setState包裹,否则UI不会感知到数据变化 - 所有Stream类型的监听,必须在
dispose生命周期中调用cancel()取消,避免页面多次进出导致重复回调、内存泄漏 - 变量命名保持一致性,避免拼写错误导致的赋值失效
内容的提问来源于stack exchange,提问作者Kaya Ryuuna
相关产品推荐
相关产品推荐

