编辑
2025-01-03
前端
00
请注意,本文编写于 126 天前,最后修改于 126 天前,其中某些信息可能已经过时。
dart
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('学生信息分页加载'), ), body: StudentList(), ), ); } } class StudentList extends StatefulWidget { @override _StudentListState createState() => _StudentListState(); } class _StudentListState extends State<StudentList> { int _page = 1; // 当前页码 final int _pageSize = 20; // 每页数据量 // 模拟从 API 获取学生数据 Future<List<String>> _fetchStudents() async { await Future.delayed(const Duration(seconds: 2)); // 模拟网络延迟 final start = (_page - 1) * _pageSize; final end = start + _pageSize; final students = List.generate(100, (index) => '学生 ${index + 1}'); final result = students.sublist(start, end > students.length ? students.length : end); _page++; // 增加页码 return result; } @override Widget build(BuildContext context) { return LoadMore( loadData: _fetchStudents, builder: (data, controller) { return ListView.builder( controller: controller, // 绑定 ScrollController itemCount: data.length, itemBuilder: (context, index) { return ListTile( title: Text(data[index]), ); }, ); }, loadingWidget: const Padding( padding: EdgeInsets.all(16.0), child: CircularProgressIndicator(), ), noMoreDataWidget: const Padding( padding: EdgeInsets.all(16.0), child: Text('没有更多学生信息了'), ), ); } } class LoadMore extends StatefulWidget { final Future<List<String>> Function() loadData; // 加载数据的回调 final Widget Function(List<String> data, ScrollController controller) builder; // 构建列表的 Widget final Widget? loadingWidget; // 加载中的 Widget final Widget? noMoreDataWidget; // 没有更多数据时的 Widget const LoadMore({ Key? key, required this.loadData, required this.builder, this.loadingWidget, this.noMoreDataWidget, }) : super(key: key); @override _LoadMoreState createState() => _LoadMoreState(); } class _LoadMoreState extends State<LoadMore> { final List<String> _data = []; // 存储加载的数据 bool _isLoading = false; // 是否正在加载 bool _hasMore = true; // 是否还有更多数据 final ScrollController _scrollController = ScrollController(); // 滚动控制器 bool _showScrollButton = false; // 是否显示滚动按钮 bool _isScrollingManually = false; // 是否正在手动滚动 @override void initState() { super.initState(); _loadData(); // 初始化时加载数据 _scrollController.addListener(_onScroll); // 监听滚动事件 } @override void dispose() { _scrollController.removeListener(_onScroll); _scrollController.dispose(); super.dispose(); } void _onScroll() { // 监听滚动位置,动态显示或隐藏按钮 if (_scrollController.position.pixels == _scrollController.position.maxScrollExtent) { setState(() { _showScrollButton = false; // 滚动到底部时隐藏按钮 }); } else { setState(() { _showScrollButton = true; // 滚动时显示按钮 }); } // 如果不是手动滚动,且滚动到底部,则触发加载更多数据 if (!_isScrollingManually && _scrollController.position.pixels == _scrollController.position.maxScrollExtent) { _loadData(); } } Future<void> _loadData() async { if (_isLoading || !_hasMore) return; setState(() { _isLoading = true; }); // 调用外部传入的加载数据方法 final newData = await widget.loadData(); setState(() { _data.addAll(newData); _isLoading = false; _hasMore = newData.isNotEmpty; // 如果没有新数据,说明没有更多数据了 }); } void _scrollToBottom() { // 标记为手动滚动 _isScrollingManually = true; // 滚动到列表底部 _scrollController.animateTo( _scrollController.position.maxScrollExtent, duration: const Duration(milliseconds: 300), curve: Curves.easeOut, ).then((_) { // 滚动完成后,取消手动滚动标记 _isScrollingManually = false; }); } @override Widget build(BuildContext context) { return Stack( children: [ NotificationListener<ScrollNotification>( onNotification: (notification) { // 监听滚动事件 if (notification is ScrollEndNotification && notification.metrics.pixels == notification.metrics.maxScrollExtent) { if (!_isScrollingManually) { _loadData(); // 滚动到底部时加载更多数据 } } return true; }, child: Column( children: [ Expanded( child: widget.builder(_data, _scrollController), // 构建列表 ), if (_isLoading) widget.loadingWidget ?? const Padding( padding: EdgeInsets.all(16.0), child: CircularProgressIndicator(), ), if (!_hasMore && _data.isNotEmpty) widget.noMoreDataWidget ?? const Padding( padding: EdgeInsets.all(16.0), child: Text('没有更多数据了'), ), ], ), ), if (_showScrollButton) Positioned( bottom: 20, right: 20, child: FloatingActionButton( onPressed: _scrollToBottom, child: const Icon(Icons.arrow_downward), ), ), ], ); } }

本文作者:yowayimono

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!