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

Flutter使用provider包调用notifyListeners后Widget未刷新问题求助

问题根因

  • 核心问题出在Selector的默认值对比逻辑:Selector默认使用==运算符判断selector返回值是否发生变化,你的代码中selector直接返回了chatCells列表的内存引用,虽然你不断往列表中新增元素,但列表本身的引用地址没有发生变化,Selector就会判定数据无更新,不会触发builder重建。
  • 次要问题:你在ViewModel构造函数中直接调用异步方法loadChats,会存在ViewModel初始化未完成时异步结果就返回的时序隐患。

修复方案

方案1:调整Selector取值逻辑(推荐,改动最小)

直接用Consumer替代Selector,只要调用notifyListeners就会触发重建,适合列表数据量不大的场景,修改Widget代码如下:

class _ConversationsState extends State<Conversations> {
  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Consumer<ConversationsViewModel>(
          builder: (context, vm, _) {
            return ListView.builder(
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              itemCount: vm.chatCells.length,
              itemBuilder: (context, index) =>
                  Text(vm.chatCells[index].message.message.body),
            );
          },
        )
      ],
    );
  }
}

方案2:保留Selector,修改对比规则

如果需要保留Selector的性能优化特性,可以将selector返回值改为可直接对比的标量值,比如列表长度:

Selector<ConversationsViewModel, int>(
  selector: (context, vm) => vm.chatCells.length,
  builder: (context, length, _) {
    final chatCells = context.read<ConversationsViewModel>().chatCells;
    return ListView.builder(
      scrollDirection: Axis.vertical,
      shrinkWrap: true,
      itemCount: length,
      itemBuilder: (context, index) =>
          Text(chatCells[index].message.message.body),
    );
  },
)

也可以修改ViewModel中列表更新逻辑,每次新增元素时生成新的列表实例,让Selector能检测到引用变化:

// ViewModel中修改chatCells定义,去掉final修饰
List<ChatCell> chatCells = [];

// 新增元素的逻辑改为
void loadChats() async {
  final chats = await chatService.getChats(userId: userId);
  for (final chat in chats) {
    final messages = await chatService.getMessages(
      userId: userId,
      chatId: chat.chatId,
      limit: 1,
    );
    final cell = ChatCell(chat: chat, message: messages.first);
    // 生成新的列表实例,修改引用地址
    chatCells = List.from(chatCells)..add(cell);
    notifyListeners();
  }
}

可选优化:调整异步调用时机

避免在ViewModel构造函数中直接调用异步方法,改为在Widget初始化完成后触发,避免时序问题:

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) {
    context.read<ConversationsViewModel>().loadChats();
  });
}

修改后可删除ViewModel构造函数中的loadChats()调用,避免重复加载。

内容的提问来源于stack exchange,提问作者Richard Robinson

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 01:15:04