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

Flutter滚动加载更多时未按预期加载5个产品的问题及解决方法

滚动加载更多时未按预期加载5个产品的问题排查与修复

问题描述

以下是实现滚动加载的Flutter代码,期望滚动到底部时每次加载5个产品,但实际触发加载时会加载更多数量:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class Scroool extends StatefulWidget {
  const Scroool({Key? key}) : super(key: key);

  @override
  State<Scroool> createState() => _ScrooolState();
}

class _ScrooolState extends State<Scroool> {
  List posts = [];
  int limit = 20;
  bool isLoadingMore = false;
  ScrollController controller = ScrollController();
  @override
  void initState() {
    fetchPosts();
    controller.addListener(scrolListener);
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey.shade300,
      body: ListView.builder(
        controller: controller,
        padding: EdgeInsets.all(20),
        itemCount: isLoadingMore ? posts.length + 1 : posts.length,
        itemBuilder: (context, index) {
         if(index < posts.length) {
           final post = posts[index];
           return Card(
             child: ListTile(
               leading: CircleAvatar(child: Text("${index + 1}"),),
               title: Text(post['title'].toString()),
               subtitle: Text(post['description']),
             ),
           );
         } else {
           return Center(child: CircularProgressIndicator(),);
         }
        },
      ),
    );
  }

  fetchPosts() async {
    final url = Uri.parse("https://dummyjson.com/products?limit=$limit");
    final response = await http.get(url);
    if(response.statusCode == 200) {
      final json = jsonDecode(response.body)['products'] as List;
      setState(() {
        posts = posts + json;
        isLoadingMore = false;
      });
    } else {
      print("ERROR");
    }
  }

  scrolListener() async{
    //don' call the api if user scroll whil calling the api not finished yet
    if(isLoadingMore) return;
   if(controller.position.pixels ==
   controller.position.maxScrollExtent) {
     setState(() {
       isLoadingMore = true;
     });
     limit+=5;
      await fetchPosts();
     setState(() {
       isLoadingMore = false;
     });
   } else {
     print("not called");
   }
  }
}

问题:当滚动到列表底部触发加载更多时,并未按照代码中设定的加载5个产品,而是加载了更多数量。

原因分析

当前逻辑的核心问题是API请求参数和数据叠加方式错误:

  • 每次触发加载时,你把limit增加5(比如从20→25→30...),然后请求limit数量的产品
  • 接着把返回的所有数据(25个、30个...)直接追加到原有列表,导致每次加载的是limit总数的产品,而非新增5个
  • 例如第一次加载20个,第二次请求25个并追加,最终列表变成20+25=45个,相当于多加载了25个而非预期的5个

修复方案

改用skip参数跳过已加载的产品,每次固定请求5个新数据:

  1. 新增skip变量记录已加载的产品数量,初始值为0
  2. 初始请求时加载20个,之后每次加载时skip累加5,固定limit=5请求新数据
  3. 调整数据处理逻辑,确保只追加新增的5个产品

修改后的完整代码

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;

class Scroool extends StatefulWidget {
  const Scroool({Key? key}) : super(key: key);

  @override
  State<Scroool> createState() => _ScrooolState();
}

class _ScrooolState extends State<Scroool> {
  List posts = [];
  final int initialLimit = 20;
  final int loadMoreLimit = 5;
  int skip = 0;
  bool isLoadingMore = false;
  ScrollController controller = ScrollController();

  @override
  void initState() {
    fetchPosts(isInitialLoad: true);
    controller.addListener(scrolListener);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey.shade300,
      body: ListView.builder(
        controller: controller,
        padding: EdgeInsets.all(20),
        itemCount: isLoadingMore ? posts.length + 1 : posts.length,
        itemBuilder: (context, index) {
          if (index < posts.length) {
            final post = posts[index];
            return Card(
              child: ListTile(
                leading: CircleAvatar(child: Text("${index + 1}")),
                title: Text(post['title'].toString()),
                subtitle: Text(post['description']),
              ),
            );
          } else {
            return const Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }

  fetchPosts({bool isInitialLoad = false}) async {
    int currentLimit = isInitialLoad ? initialLimit : loadMoreLimit;
    final url = Uri.parse("https://dummyjson.com/products?limit=$currentLimit&skip=$skip");
    final response = await http.get(url);
    
    if (response.statusCode == 200) {
      final json = jsonDecode(response.body)['products'] as List;
      setState(() {
        posts.addAll(json);
        isLoadingMore = false;
        // 更新skip,下次请求跳过已加载的数量
        skip += json.length;
      });
    } else {
      print("ERROR");
      setState(() {
        isLoadingMore = false;
      });
    }
  }

  scrolListener() async {
    if (isLoadingMore) return;
    
    if (controller.position.pixels == controller.position.maxScrollExtent) {
      setState(() {
        isLoadingMore = true;
      });
      await fetchPosts();
    } else {
      print("not called");
    }
  }
}

关键修改点

  • 新增initialLimit(初始加载20个)和loadMoreLimit(每次加载5个)常量,明确区分初始和加载更多的数量
  • 新增skip变量,每次请求后累加已加载的产品数量,确保下次请求跳过已有的数据
  • 修改fetchPosts方法,根据是否是初始加载选择对应的limit,并带上skip参数
  • 使用posts.addAll(json)替代posts = posts + json,写法更简洁高效
  • 加载失败时也重置isLoadingMore,避免加载状态卡死

内容的提问来源于stack exchange,提问作者Osama Mohammed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 13:20:30