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

Flutter 中如何为Wrap布局的商品列表实现无限滚动加载功能?

Flutter 标签页滚动加载改造方案
  • 第一步:将StatelessWidget改为StatefulWidget,滚动加载需要维护页码、加载状态、商品数据等可变状态,无状态组件无法实现。
  • 第二步:声明状态变量,包括当前分页页码、是否加载中标识、是否已加载完全部数据标识、存储商品数据的列表、滚动控制器ScrollController。
  • 第三步:在initState中给滚动控制器添加监听,判断滚动位置距离底部小于阈值时,触发下一页数据加载逻辑,加载过程中要锁死加载状态避免重复请求。
  • 第四步:替换原有的固定List.generate逻辑,改为通过商品列表动态渲染,列表末尾根据加载状态渲染加载中提示或无更多数据提示。
  • 第五步:如果商品总数较大,建议将Wrap替换为GridView.builder,懒加载渲染组件性能更优。
class ProductList extends StatefulWidget {
  const ProductList({Key? key}) : super(key: key);

  @override
  State<ProductList> createState() => _ProductListState();
}

class _ProductListState extends State<ProductList> {
  final ScrollController _scrollController = ScrollController();
  List<dynamic> productList = []; // 替换为实际商品数据模型
  int _currentPage = 1;
  bool _isLoading = false;
  bool _hasMore = true;
  final int _pageSize = 10;

  @override
  void initState() {
    super.initState();
    // 初始化加载第一页数据
    _loadProductList();
    // 添加滚动监听
    _scrollController.addListener(() {
      if (_isLoading || !_hasMore) return;
      // 滚动到距离底部不足100px时触发加载
      if (_scrollController.position.pixels >= _scrollController.position.maxScrollExtent - 100) {
        _loadMoreProductList();
      }
    });
  }

  // 加载第一页数据
  Future<void> _loadProductList() async {
    setState(() => _isLoading = true);
    // 这里替换为实际的接口请求
    final newData = await Future.delayed(const Duration(milliseconds: 500), () {
      return List.generate(_pageSize, (index) => index);
    });
    setState(() {
      productList = newData;
      _currentPage = 1;
      _hasMore = newData.length >= _pageSize;
      _isLoading = false;
    });
  }

  // 加载更多数据
  Future<void> _loadMoreProductList() async {
    setState(() => _isLoading = true);
    // 这里替换为实际的接口请求,传入_currentPage+1
    final newData = await Future.delayed(const Duration(milliseconds: 500), () {
      return List.generate(_pageSize, (index) => productList.length + index);
    });
    setState(() {
      productList.addAll(newData);
      _currentPage += 1;
      _hasMore = newData.length >= _pageSize;
      _isLoading = false;
    });
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      controller: _scrollController,
      child: Wrap(
        direction: Axis.horizontal,
        alignment: WrapAlignment.start,
        crossAxisAlignment: WrapCrossAlignment.start,
        runAlignment: WrapAlignment.start,
        runSpacing: 8.0,
        spacing: 15.0,
        children: [
          ...List.generate(productList.length, (index) {
            return SizedBox(
              width: getProportionateScreenWidth(160),
              child: GestureDetector(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.start,
                  children: [
                    AspectRatio(
                      aspectRatio: 1.02,
                      child: Container(
                          padding: EdgeInsets.all(getProportionateScreenWidth(5)),
                          decoration: BoxDecoration(
                            color: kSecondaryColor.withOpacity(0.3),
                            borderRadius: BorderRadius.circular(15),
                          ),
                          child: null),
                    ),
                    const SizedBox(height: 10),
                  ],
                ),
              ),
            );
          }),
          // 底部加载状态提示
          if (_isLoading)
            const Padding(
              padding: EdgeInsets.symmetric(vertical: 16),
              child: Center(child: CircularProgressIndicator()),
            ),
          if (!_hasMore && productList.isNotEmpty)
            const Padding(
              padding: EdgeInsets.symmetric(vertical: 16),
              child: Center(child: Text('没有更多商品了')),
            )
        ],
      ),
    );
  }
}

额外优化点:可按需添加接口请求失败的重试逻辑、下拉刷新逻辑,数据量超过50条时建议替换为GridView.builder减少内存占用。

内容的提问来源于stack exchange,提问作者2421zeaman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 08:06:03