Flutter GridView分页异常:加载第二页后无法回滚至第一页
解决GridView.builder分页后无法滚动回第一页的问题
我帮你定位到了问题的核心,咱们一步步来修复:
问题根源
你在加载下一页数据时犯了两个关键错误:
- 清空了原有数据:API调用函数
getSearchResult里的search_result_list.clear();会把第一页的20条数据全部删掉,导致加载第二页后列表只剩1条数据,自然滚不回第一页。 - 错误的数据添加逻辑:你声明了
tList但没往里面填充新数据,反而用search_result_list.addAll(tList);——这相当于啥也没加,反而把旧数据清了,雪上加霜。
另外,你还缺少是否还有更多数据的判断,会导致滚动到底部时重复触发无效请求。
修复步骤与代码修改
1. 补充必要变量
先在State类里加两个状态变量:
bool isLoading = false; bool hasMore = true; // 标记是否还有更多数据可以加载
2. 修正API数据加载逻辑
修改getSearchResult函数,移除清空原有数据的代码,正确处理新数据的追加:
Future<void> getSearchResult(int index) async { print("size" + index.toString()); // 只有未加载中且还有更多数据时,才发起请求 if (!isLoading && hasMore) { setState(() { isLoading = true; }); List<PrefrenceModel> tList = []; // 用来存储新获取的一页数据 try { var response = await http.get( "${Urls.baseUrl}${Urls.CategorySearch}?latitude=${Constants.latitude}&longitude=${Constants.longitude}&radius=100&locale=en&keyword=${slug}&page=${index.toString()}&type=item", headers: { "Content-Type": "application/json", "Authorization": "Bearer ${userToken}", "Accept": "application/json" }, ); if (response.statusCode == 200) { Map<String, dynamic> value = json.decode(response.body); var status = value['success']; var message = value['message']; last_page = value['last_page']; if (status == true) { var data = value['data']['data']; if (data.length > 0) { // 把新获取的数据填充到tList for (int i = 0; i < data.length; i++) { var obj = data[i]; tList.add(PrefrenceModel( obj['id'].toString(), obj['description'].toString(), obj['valid_from'].toString(), obj['valid_to'].toString(), obj['thumbs']['md'].toString(), obj['status'].toString(), "", "", obj['name'].toString(), "", obj['flyer_slug'].toString(), obj['type'].toString(), obj['isClipped'], obj['url'].toString(), obj['price'].toString(), obj['flyer_page_id'].toString(), obj['id'].toString(), obj['shop_name'].toString(), obj['shop_logo'].toString(), obj['brand_name'].toString(), obj['brand_logo'].toString(), )); } setState(() { isLoading = false; search_result_list.addAll(tList); // 把新数据追加到原有列表后面 page++; // 判断是否还有更多数据可加载 if (page > last_page) { hasMore = false; } }); } else { setState(() { isLoading = false; hasMore = false; }); final snackBar = SnackBar(content: Text("No Data Available")); _scaffoldKey.currentState?.showSnackBar(snackBar); } } else { setState(() { isLoading = false; }); print("Error..."); final snackBar = SnackBar(content: Text(message)); _scaffoldKey.currentState?.showSnackBar(snackBar); } } else { setState(() { isLoading = false; }); var value = json.decode(response.body); var message = value['message']; final snackBar = SnackBar(content: Text(message)); _scaffoldKey.currentState?.showSnackBar(snackBar); } } catch (e) { setState(() { isLoading = false; }); print(e.toString()); } } }
3. 修正GridView的itemCount与加载指示器逻辑
修改GridView.builder的itemCount,当没有更多数据时,不再显示加载指示器:
GridView.builder( controller: _sccontroller, // 只有还有更多数据时,才多留一个加载指示器的位置 itemCount: hasMore ? search_result_list.length + 1 : search_result_list.length, physics: const AlwaysScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( childAspectRatio: 5.2 / 8.0, crossAxisCount: 2, crossAxisSpacing: 3.0, mainAxisSpacing: 3.0, ), itemBuilder: (BuildContext context, int index) { // 只有还有更多数据且是最后一个位置时,才显示加载指示器 if (hasMore && index == search_result_list.length) { return Center( child: Padding( padding: const EdgeInsets.all(8.0), child: CircularProgressIndicator(), ), ); } else { // 原有Card渲染逻辑保持不变 return Container( child: Padding( padding: const EdgeInsets.only(top:10), child: GestureDetector( onTap: () { }, child: Container( child: Card( elevation: 2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(2), ), child: Container( width: 300, child: Stack( children: [ Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: EdgeInsets.all(2), child: Image.network( Urls.baseImageUrl + search_result_list[index].thumb_path, fit: BoxFit.fill, height: 180, width: 180, ), ), Padding( padding: const EdgeInsets.all(5), child: Align( child: Text( search_result_list[index].name, style: TextStyle( fontSize: 11, fontWeight: FontWeight.bold), textAlign: TextAlign.left, ), alignment: Alignment.centerLeft, )), ], ), ] )), ), ), ), ), ); } }, )
4. 优化滚动监听逻辑
在initState的滚动监听里,加上hasMore判断,避免无效触发:
@override void initState() { super.initState(); Future token = SharedPrefrence().getToken(); token.then((data) async { userToken = data; this.getSearchResult(page); _sccontroller.addListener(() { // 只有还有更多数据且滚动到底部时,才加载下一页 if (_sccontroller.position.pixels == _sccontroller.position.maxScrollExtent && hasMore) { getSearchResult(page); } }); }); setState(() { title = name; }); }
为什么ListView没问题?
其实ListView里如果用了同样的错误逻辑,也会出现一样的问题——可能你在ListView的实现里没加search_result_list.clear();,或者数据处理逻辑不同,才没暴露这个问题。
内容的提问来源于stack exchange,提问作者Abhijith
相关产品推荐
相关产品推荐

