Flutter教程:如何为Future Builder的ListView添加分页功能
Flutter 列表分页实现(适配你的业务场景)
核心问题拆解
你的代码无法实现分页加载的核心原因:
- 没有维护本地数据集,每次请求都是重新获取数据而非追加到现有列表
- API请求未携带分页参数,无法获取对应页码的数据
- 缺少加载状态控制,容易重复触发加载请求
- 未实现正确的加载更多逻辑,且存在方法调用语法错误
修改后的完整代码
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:xsocial/utils/my_drawer.dart'; import 'package:xsocial/utils/transaction_history_tile.dart'; import 'package:http/http.dart' as http; class TransactionHistory extends StatefulWidget { const TransactionHistory({super.key}); @override State<TransactionHistory> createState() => _TransactionHistoryState(); } class _TransactionHistoryState extends State<TransactionHistory> { final _controller = ScrollController(); int _currentPage = 1; List<dynamic> _transactions = []; bool _isLoading = false; // 带分页参数的API请求方法 Future<List> _getTransactionData(int page) async { final res = await http.get(Uri.parse( 'http://194.143.45.195/myappi/cash-transaction-history?page=$page')); var resBody = jsonDecode(res.body); return resBody; } // 加载更多逻辑,处理数据追加、状态切换与错误提示 Future<void> _loadMoreTransactions() async { if (_isLoading) return; // 防止重复加载 setState(() => _isLoading = true); try { final newTransactions = await _getTransactionData(_currentPage); if (newTransactions.isNotEmpty) { setState(() { _transactions.addAll(newTransactions); _currentPage++; }); } else { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('已加载全部交易记录')), ); } } } catch (e) { if (mounted) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('加载失败:${e.toString()}')), ); } } finally { if (mounted) { setState(() => _isLoading = false); } } } @override void initState() { super.initState(); _loadMoreTransactions(); // 初始化加载第一页 _controller.addListener(() { // 精准判断滚动到底部 if (_controller.position.pixels == _controller.position.maxScrollExtent) { _loadMoreTransactions(); } }); } @override void dispose() { _controller.dispose(); // 清理控制器,避免内存泄漏 super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text( 'Transaction History', style: TextStyle(fontWeight: FontWeight.bold), ), ), drawer: const MyDrawer(), body: Column( mainAxisSize: MainAxisSize.max, children: [ // 搜索栏 const SizedBox(height: 15.0), Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: Container( decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(7), ), child: const TextField( decoration: InputDecoration( prefixIcon: Icon(Icons.search), border: InputBorder.none, hintText: 'Search transactions by ID', ), ), ), ), const SizedBox(height: 15.0), // 交易列表区域 Expanded( child: _transactions.isEmpty && !_isLoading ? const Center(child: Text('暂无交易记录')) : ListView.builder( controller: _controller, itemCount: _transactions.length + (_isLoading ? 1 : 0), itemBuilder: (context, index) { if (index == _transactions.length) { // 底部加载指示器 return const Padding( padding: EdgeInsets.symmetric(vertical: 16.0), child: Center(child: CircularProgressIndicator()), ); } // 适配你的TransactionHistoryTile组件,若原组件接收列表可改为list: [_transactions[index]] return TransactionHistoryTileItem( transaction: _transactions[index]); }, ), ), ], ), ); } }
关键修改说明
- 本地状态管理:新增
_transactions列表存储所有已加载数据,_isLoading控制加载状态,避免重复请求 - 分页API适配:修改请求方法,添加
page参数,确保后端返回对应页码的数据 - 加载逻辑优化:实现
_loadMoreTransactions方法,处理数据追加、错误捕获与用户提示 - 滚动监听调整:改用
maxScrollExtent判断底部,避免顶部边缘的误触发 - 列表展示优化:用
ListView.builder替代单纯的FutureBuilder,分页场景下本地维护状态更灵活,同时添加无数据、加载中的状态提示 - 内存泄漏防护:在
dispose方法中销毁ScrollController
内容的提问来源于stack exchange,提问作者Cliff Odebala Clarke
相关产品推荐
相关产品推荐

