Flutter使用pagination_view时如何不刷新全列表更新单个分页条目
问题背景
- Flutter项目中使用
pagination_view库实现商品网格列表分页加载,当前基础分页、上拉加载、下拉刷新功能均运行正常 - 待实现需求:点击列表项右上角的心形收藏图标时,仅切换当前被点击条目的收藏状态、切换对应图标显示,不需要刷新整个分页列表的所有条目
- 实现效果参考:

现有代码
分页列表组件代码
Expanded( flex: 1, child: Padding( padding: const EdgeInsets.all(5.0), child: PaginationView < ProductList > ( key: key, paginationViewType: PaginationViewType.gridView, scrollDirection: Axis.vertical, pageFetch: (page) => pageFetch(page), pullToRefresh: true, itemBuilder: (BuildContext context, ProductList user, int index) { return GridTile( child: Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15.0), ), child: Column( children: [ Align( alignment: Alignment.topRight, child: Padding( padding: const EdgeInsets.all(8.0), child: InkWell( onTap: () => { controller.addToWishList({ "ProductId": user.productId }), key }, child: Image.asset( height: 20, width: 20, user.flag ? "assets/images/ic_like_heart.png" : "assets/images/ic_unlike_heart.png")), ), ), Expanded( flex: 1, child: CachedNetworkImage( imageUrl: user.image, imageBuilder: (context, imageProvider) => Container( decoration: BoxDecoration( image: DecorationImage( image: imageProvider, fit: BoxFit.contain, ), ), ), progressIndicatorBuilder: (context, url, progress) => Center( child: CircularProgressIndicator( value: progress.progress, ), ), errorWidget: (context, url, error) => const Icon(Icons.warning_amber_rounded), ), ), Padding( padding: const EdgeInsets.all(8.0), child: Text(user.productName), ) ], ), ), ); }, onError: (dynamic error) => const Center( child: Text( 'Some error occurred', style: TextStyle(fontSize: 18), ), ), onEmpty: const Center( child: Text( 'Sorry! This is empty', style: TextStyle(fontSize: 18), ), ), bottomLoader: const Center( child: CircularProgressIndicator(), ), initialLoader: const Padding( padding: EdgeInsets.all(8.0), child: Center( child: CircularProgressIndicator( backgroundColor: Colors.transparent), ), ), ), ), )
分页数据拉取方法代码
Future < List < ProductList >> pageFetch(int offset) async { page = (offset / 10).round() + 1; _logger.d("Offset number::$offset Page number::$page"); var data = { 'Category': '', 'Search': _search, 'PageNumber': page, 'PageCount': 10 }; return await controller.getProductsList(data, page); }
实现方案
核心逻辑:把单个商品条目抽成独立的StatefulWidget,每个条目自己维护自己的收藏状态,点击时只触发当前条目的局部刷新,完全不会影响列表其他项,也不会重建整个分页组件。
- 第一步:抽离独立的商品项组件
把原来写在itemBuilder里的布局代码挪到单独的StatefulWidget中,本地维护收藏状态,初始化时直接用接口返回的收藏标识赋值:
class ProductGridItem extends StatefulWidget { final ProductList product; final Function(Map<String, dynamic>) addToWishList; const ProductGridItem({ super.key, required this.product, required this.addToWishList, }); @override State<ProductGridItem> createState() => _ProductGridItemState(); } class _ProductGridItemState extends State<ProductGridItem> { // 本地维护当前条目的收藏状态,初始化用接口返回的flag值 late bool isFavorited; @override void initState() { super.initState(); isFavorited = widget.product.flag; } @override Widget build(BuildContext context) { return GridTile( child: Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15.0), ), child: Column( children: [ Align( alignment: Alignment.topRight, child: Padding( padding: const EdgeInsets.all(8.0), child: InkWell( onTap: () { // 先本地切换状态更新UI,点击无等待感 setState(() { isFavorited = !isFavorited; }); // 调用接口提交收藏操作 widget.addToWishList({ "ProductId": widget.product.productId }); }, child: Image.asset( height: 20, width: 20, isFavorited ? "assets/images/ic_like_heart.png" : "assets/images/ic_unlike_heart.png" ), ), ), ), Expanded( flex: 1, child: CachedNetworkImage( imageUrl: widget.product.image, imageBuilder: (context, imageProvider) => Container( decoration: BoxDecoration( image: DecorationImage( image: imageProvider, fit: BoxFit.contain, ), ), ), progressIndicatorBuilder: (context, url, progress) => Center( child: CircularProgressIndicator( value: progress.progress, ), ), errorWidget: (context, url, error) => const Icon(Icons.warning_amber_rounded), ), ), Padding( padding: const EdgeInsets.all(8.0), child: Text(widget.product.productName), ) ], ), ), ); } }
- 第二步:替换原itemBuilder实现
原来的itemBuilder里不需要再写大段布局,直接返回抽离好的组件即可:
itemBuilder: (BuildContext context, ProductList product, int index) { return ProductGridItem( product: product, addToWishList: controller.addToWishList, ); }
补充说明:如果收藏接口调用失败需要回滚状态,可以给ProductGridItem设置全局Key,接口报错时通过key拿到当前组件的state,把收藏状态改回点击前的值即可,全程不会触发整个列表刷新。另外原代码onTap回调最后写的
key是无效冗余代码,直接删掉即可。
内容的提问来源于stack exchange,提问作者Kaushik bhut
相关产品推荐
相关产品推荐

