在 Flutter 里实现「下拉刷新 + 上拉加载更多」最常用、最省心的组合就是 RefreshIndicator(官方下拉)+ ScrollController(监听上拉)。下面给出完整可直接运行的最小示例,并补充 3 种常见需求变体,复制即可用。
一、最小可运行示例(ListView.separated 版)
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: 'Refresh&LoadMore',
theme: ThemeData(useMaterial3: true),
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final ScrollController _ctrl = ScrollController();
final List<int> _data = List.generate(15, (i) => i);
bool _isLoading = false; // 上拉加载状态
bool _noMore = false; // 数据到底标记
void initState() {
super.initState();
_ctrl.addListener(_onScroll);
}
void dispose() {
_ctrl.dispose();
super.dispose();
}
/* 核心:上拉检测 */
void _onScroll() {
if (_ctrl.position.pixels >= _ctrl.position.maxScrollExtent – 100 &&
!_isLoading &&
!_noMore) {
_loadMore();
}
}
/* 下拉刷新 */
Future<void> _onRefresh() async {
await Future.delayed(const Duration(seconds: 1)); // 模拟网络
_noMore = false;
_data.clear();
_data.addAll(List.generate(15, (i) => i));
if (mounted) setState(() {});
}
/* 上拉加载 */
Future<void> _loadMore() async {
_isLoading = true;
if (mounted) setState(() {});
await Future.delayed(const Duration(seconds: 1));
final append = List.generate(10, (i) => _data.length + i);
_data.addAll(append);
_isLoading = false;
if (_data.length > 60) _noMore = true; // 模拟到底
if (mounted) setState(() {});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('下拉刷新 + 上拉加载')),
body: RefreshIndicator(
onRefresh: _onRefresh,
child: ListView.separated(
controller: _ctrl,
padding: const EdgeInsets.all(12),
itemCount: _data.length + 1, // +1 给底部 loading / 到底提示
separatorBuilder: (_, __) => const Divider(height: 16),
itemBuilder: (context, index) {
if (index == _data.length) {
return _buildFooter(); // 底部组件
}
return ListTile(
tileColor: Colors.blueGrey.shade50,
title: Text('Item ${_data[index]}'),
);
},
),
),
);
}
/* 底部:加载中 / 无更多 / 空容器 */
Widget _buildFooter() {
if (_noMore) {
return const Padding(
padding: EdgeInsets.all(16),
child: Center(child: Text('—— 到底了 ——')),
);
}
if (_isLoading) {
return const Padding(
padding: EdgeInsets.all(16),
child: Center(child: CircularProgressIndicator(strokeWidth: 2)),
);
}
return const SizedBox.shrink();
}
}
运行效果: 下拉出现圆形进度 → 数据重置;滑到底部自动触发加载 → 底部出现小圆圈;加载到 60 条后提示“到底了”。
二、3 种常见变体
dependencies:
flutter_easyrefresh: ^4.0.0
EasyRefresh(
onRefresh: () async => ...,
onLoad: () async => ...,
child: ListView(...),
)
CustomScrollView(
slivers: [
CupertinoSliverRefreshControl(onRefresh: ...),
SliverList(delegate: SliverChildBuilderDelegate(...)),
if (_isLoading)
const SliverToBoxAdapter(
child: Center(child: CircularProgressIndicator())),
],
)
三、踩坑提醒
把上面模板直接复制进项目即可跑通,再根据业务替换网络请求、分页逻辑、空视图、错误视图即可。祝编码愉快!
网硕互联帮助中心





评论前必须登录!
注册