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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:09:42