Flutter中基于Provider属性实现无闪烁无限滚动有没有更简洁方案?
优化方案
- 第一步:移除自定义广播StreamController,用
Selector精准监听列表长度
不需要额外维护流通知长度,直接用Provider自带的Selector监听列表的length属性,只有长度变化时才会触发ListView的外层builder重建,其他情况下父组件完全不重绘,从根源避免闪烁。
核心代码示例:Selector<ImageListViewModel, int>( selector: (context, vm) => vm.imageList.length, builder: (context, listLength, child) { return ListView.builder( controller: _scrollController, itemCount: listLength + (vm.isLoading ? 1 : 0), itemBuilder: (context, index) { if (index == listLength) { // 底部加载占位 return const CircularProgressIndicator(); } return ImageItem(index: index); }, ); }, ) - 第二步:绑定ScrollController实现自动触发下一页加载
给ListView绑定滚动监听,当滚动位置距离底部小于预设阈值(比如剩余3个item的高度)时,通知ViewModel触发下一页的Firestore查询,不需要手动维护拉取时机。
核心代码示例:late final ScrollController _scrollController; @override void initState() { super.initState(); _scrollController = ScrollController(); _scrollController.addListener(() { if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 300) { context.read<ImageListViewModel>().loadNextPage(); } }); } - 第三步:列表项组件独立监听对应索引的数据
每个列表项内部自己用Selector监听对应索引的单条数据,只有当前索引的数据更新时才会重绘自身,完全不影响其他列表项和父容器。
核心代码示例:class ImageItem extends StatelessWidget { final int index; const ImageItem({super.key, required this.index}); @override Widget build(BuildContext context) { return Selector<ImageListViewModel, ImageItemModel>( selector: (context, vm) => vm.imageList[index], shouldRebuild: (prev, next) => prev != next, builder: (context, item, child) { return Image.network(item.url, gaplessPlayback: true); }, ); } } - 第四步:ViewModel层维护Firestore流与分页状态
ViewModel内部只需要维护imageList列表、isLoading状态、lastDocument快照三个核心属性,监听Firestore的流更新时直接拼接新数据到imageList,调用notifyListeners即可,不需要额外的流广播逻辑,符合标准MVVM的分层规则。
补充注意点:Image组件可以加
gaplessPlayback: true配置,避免图片复用过程中出现的短暂白屏,进一步消除视觉闪烁。
内容的提问来源于stack exchange,提问作者firestreak
相关产品推荐
相关产品推荐

