Flutter ListView.builder添加头部消息时子组件重复重建问题求助
我正在实现一个包含消息列表的聊天界面,使用ListView.builder并通过Bloc管理消息列表。当添加新消息时,Bloc会发出包含旧消息和新消息的新状态列表。当前将新消息添加到列表头部时(代码:emit(state.copyWith(messages: [event.message, ...state.messages ]))),旧消息索引变化导致ListView重建所有子组件;而将新消息添加到尾部时无此问题,但我的需求必须将消息添加到头部。请问有什么方法可以避免ListView每次消息列表变化时重建所有子组件?
我的ListView代码如下:
BlocBuilder<DiscussionBloc, DiscussionState>( buildWhen: (previous, current) => previous.messages != current.messages, builder: (context, newState) { return ListView.builder( padding: EdgeInsets.only(bottom: 10.h), shrinkWrap: true, reverse: true, itemCount: newState.hasReachedMax ? newState.messages.length : newState.messages.length + 1, itemBuilder: (builder, index) => _buildMessageBubble( context, index, widget.state.messages, widget.state.conversation, widget.state.message, widget.state.isMessageLoading), ); }, ),
给消息项绑定唯一Key
给_buildMessageBubble返回的组件加上基于消息唯一ID的ValueKey,让ListView能识别出哪些是已有的组件。只要消息内容没变化,哪怕索引变了,也不会重新渲染。
先修改itemBuilder里的调用,传入当前索引对应的消息:itemBuilder: (builder, index) => _buildMessageBubble( context, index, widget.state.messages[index], // 传入当前消息 widget.state.conversation, widget.state.message, widget.state.isMessageLoading),然后在
_buildMessageBubble内部添加Key:Widget _buildMessageBubble(BuildContext context, int index, Message message, ...) { return Container( key: ValueKey(message.id), // 用消息唯一ID作为Key // 消息气泡的其他布局代码 ); }利用reverse属性调整消息添加位置
你已经设置了reverse: true,这个属性会让列表从底部往上展示。那直接把新消息添加到列表尾部,界面上就会显示在顶部,刚好符合需求。而且这种方式不会改变旧消息的索引,ListView只会渲染新增的那一项,完全避免全量重建。修改Bloc里的emit代码:emit(state.copyWith(messages: [...state.messages, event.message ]))这是最省心高效的方案,推荐优先用这个。
优化BlocBuilder的buildWhen判断
当前buildWhen是对比消息列表的引用,每次新增消息都会触发重建。可以改成对比消息数量或者新增消息的ID,减少不必要的BlocBuilder刷新:buildWhen: (previous, current) => previous.messages.length != current.messages.length || (current.messages.isNotEmpty && current.messages.first.id != previous.messages.first.id),
内容的提问来源于stack exchange,提问作者Dali Hmida

