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
相关产品推荐
相关产品推荐

