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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 15:45:03