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

Flutter使用ChangeNotifier实现推送消息列表无法更新重建页面问题

问题:推送消息列表无法更新并重建HomePage

我想实现一个推送消息列表,但找不到更新列表并重建HomePage的方法。核心问题出在这两行代码:

final MessageModel messageModel = MessageModel();

messageModel.addMessage(message);

main() 代码

import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:notification_exemple_4/version/v1/pages/home_page.dart';
import 'package:notification_exemple_4/version/v1/pages/notification_page.dart';
import 'package:provider/provider.dart';

import 'api/firebase_api.dart';
import 'firebase_options.dart';

final navigatorKey = GlobalKey<NavigatorState>();

class MessageModel extends ChangeNotifier {
  List<Widget> _messaging = [];
  List<Widget> get messaging => _messaging;

  void addMessage(RemoteMessage remoteMessage) {
    messaging.add(Text(remoteMessage.messageId.toString()));
    notifyListeners();
  }
}

final MessageModel messageModel = MessageModel();

@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message,) async {
  // 如果要在后台使用其他Firebase服务,比如Firestore,
  // 确保在使用前调用`initializeApp`
  await Firebase.initializeApp();

  print("Handling a background message: ${message.messageId}");
}

Future<void> main() async{
  WidgetsFlutterBinding.ensureInitialized();
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    print('Got a message whilst in the foreground!');
    print('Message data: ${message.data}');

    if (message.notification != null) {
      messageModel.addMessage(message);
      print("tp failed");
      /*navigatorKey.currentState?.pushNamed(
        '/home',
        arguments: message,
      );*/
      print('Message also contained a notification: ${message.notification}');
    }
  });


  await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
  await FirebaseApi().iniNotifications();

  runApp(
      MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => MessageModel()),
        ],
        child: const MyApp(),
      )
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
      navigatorKey: navigatorKey,
      routes: {
        "/home": (context) => const HomePage(),
        "/notification_screen": (context) => const NotificationPage(),
      },
    );
  }
}

我用MessageModel存储消息列表做测试展示,同时通过MultiProvider注册了MessageModel的Provider,但又声明了全局的MessageModel实例,在FirebaseMessaging的onMessage监听里调用该实例的addMessage方法。

HomePage() 代码

import 'package:flutter/material.dart';
import 'package:notification_exemple_4/version/v1/main.dart';
import 'package:provider/provider.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  @override
  Widget build(BuildContext context) {
    print(messageModel.messaging);

    return Scaffold(
      appBar: AppBar(title: const Text("Home Page"),),
      body: SafeArea(
        child: ListView(
          children: context.watch<MessageModel>().messaging,
        )
      ),
    );
  }
}

HomePage里调用消息列表用ListView展示,已经把read()改成watch()了。我尝试用Provider包,但onMessage.listen()里没有context,拿不到Provider实例,导致无法触发HomePage重建。


解决方案

问题根源在于你维护了两个独立的MessageModel实例:

  1. 全局声明的messageModel实例,你在onMessage监听中调用它的addMessage
  2. MultiProvider创建的全新MessageModel实例,HomePage通过context.watch<MessageModel>()监听的是这个实例

两者完全无关,所以全局实例的notifyListeners()无法触发HomePage的重建。

修复步骤:

  1. 删除全局的MessageModel实例,移除main里的final MessageModel messageModel = MessageModel();声明

  2. 调整监听时机,获取Provider实例
    把onMessage监听移到runApp之后,通过全局navigatorKey获取上下文来拿到Provider实例:

    Future<void> main() async{
      WidgetsFlutterBinding.ensureInitialized();
      await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
      await FirebaseApi().iniNotifications();
    
      FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
    
      runApp(
          MultiProvider(
            providers: [
              ChangeNotifierProvider(create: (_) => MessageModel()),
            ],
            child: const MyApp(),
          )
      );
    
      // 延迟确保Provider初始化完成
      Future.microtask(() {
        FirebaseMessaging.onMessage.listen((RemoteMessage message) {
          print('Got a message whilst in the foreground!');
          print('Message data: ${message.data}');
    
          if (message.notification != null) {
            // 通过navigatorKey获取上下文,操作Provider管理的实例
            final messageModel = Provider.of<MessageModel>(navigatorKey.currentContext!, listen: false);
            messageModel.addMessage(message);
            print('Message also contained a notification: ${message.notification}');
          }
        });
      });
    }
    
  3. 优化MessageModel设计
    不要直接存储Widget,改为存储消息数据,在HomePage中根据数据构建UI,更符合状态管理最佳实践:

    class MessageModel extends ChangeNotifier {
      List<RemoteMessage> _messages = [];
      List<RemoteMessage> get messages => _messages;
    
      void addMessage(RemoteMessage remoteMessage) {
        _messages.add(remoteMessage);
        notifyListeners();
      }
    }
    

    同步修改HomePage:

    @override
    Widget build(BuildContext context) {
      final messages = context.watch<MessageModel>().messages;
    
      return Scaffold(
        appBar: AppBar(title: const Text("Home Page"),),
        body: SafeArea(
          child: ListView.builder(
            itemCount: messages.length,
            itemBuilder: (context, index) {
              return Text(messages[index].messageId.toString());
            },
          )
        ),
      );
    }
    
  4. 移除HomePage对全局实例的引用
    删除print(messageModel.messaging);这行代码,避免引用不存在的全局实例。

修改完成后,onMessage监听中操作的就是Provider管理的同一个MessageModel实例,调用addMessage后notifyListeners()会触发HomePage的重建,消息列表就能正常更新。


内容的提问来源于stack exchange,提问作者Mr. Munsch

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 14:35:00