【Flutter For OpenHarmony】鸿蒙列表交互实战:下拉刷新与上拉加载更多
本文介绍了在OpenHarmony平台上使用easy_refresh库实现高性能分页加载功能的方法。通过改造Service层支持分页参数,在Provider层管理分页状态,并在UI层集成EasyRefresh组件,成功实现了下拉刷新和上拉加载功能。文章详细讲解了从数据获取到UI展示的完整流程,包括添加依赖、核心代码改造以及状态管理的关键实现。最终效果优化了用户体验,解决了数据一次性加载和交互死板的
摘要:在上一阶段我们实现了基础的网络请求与列表展示,但对于一个成熟的移动端应用来说,仅仅展示数据是远远不够的。本文的核心任务是优化用户体验,引入现代化的列表交互方式——下拉刷新(Pull-to-Refresh)与上拉加载更多(Infinite Scroll)。本文将详细讲解如何使用
easy_refresh库在 OpenHarmony 平台上实现高性能的分页加载功能。
前言
在上文中,我们成功请求到了 API 数据并展示在列表中。然而,当时的实现存在两个明显的缺陷:
- 数据一次性加载:如果服务器有 1000 条数据,一次性拉取会消耗大量流量和内存,甚至导致应用卡顿。
- 交互死板:用户无法手动更新数据,也无法浏览后续的内容。
为了解决这些问题,我们需要引入分页机制。
一、 技术选型与依赖配置
在 Flutter 生态中,实现刷新加载的方案有很多(如官方的 RefreshIndicator),但为了同时支持“下拉刷新”和“上拉加载”,且拥有更好的自定义能力,我们选择 easy_refresh 这个成熟的第三方库。
1. 添加依赖
打开项目根目录下的 pubspec.yaml,添加 easy_refresh 依赖:
dependencies:
flutter:
sdk: flutter
dio: ^5.9.0
provider: ^6.1.5+1
easy_refresh: 3.3.0 # 3.4.0 版本与部分旧版 Flutter SDK 不兼容,建议锁定 3.3.0

添加后,记得在终端运行 flutter pub get 以安装依赖。
二、 核心代码改造
我们要从底层的数据获取开始,一直改造到顶层的 UI 展示,涉及 Service -> Provider -> UI。
1. Service 层:支持分页参数
服务端通常通过 page(页码)或 offset(偏移量)来控制返回的数据。我们使用的 JSONPlaceholder API 支持 _start 和 _limit 参数。
修改文件:lib/services/api_service.dart
// 原有的 fetchPosts() 方法只支持获取全部
// 修改为支持传入 start 和 limit 参数
Future<List<Post>> fetchPosts({int start = 0, int limit = 10}) async {
try {
final response = await _dio.get(
'$_baseUrl/posts',
queryParameters: {
'_start': start, // 起始位置
'_limit': limit, // 每页条数
},
);
// ... 解析逻辑保持不变
} catch (e) {
// ... 错误处理逻辑保持不变
}
}
2. Provider 层:管理分页状态
Provider 需要记录当前加载到了第几页,是否还有更多数据,并区分“刷新”和“加载更多”两种行为。
修改文件:lib/providers/post_provider.dart
我们引入了几个关键变量:
_page: 当前页码(或倍数)。_hasMore: 标记是否还有数据,用于控制是否显示“没有更多了”。_limit: 每页加载的数量(设为 10)。
class PostProvider extends ChangeNotifier {
// ... 其他变量
// 分页相关状态
bool _hasMore = true;
int _page = 0;
final int _limit = 10;
bool get hasMore => _hasMore;
// 下拉刷新:重置所有状态
Future<void> refresh() async {
_isLoading = true;
_page = 0; // 重置页码
_hasMore = true; // 重置更多标记
notifyListeners();
try {
// 获取第一页数据
final newPosts = await _apiService.fetchPosts(start: 0, limit: _limit);
_posts = newPosts; // 覆盖原有数据
_page = 1; // 页码递增
if (newPosts.length < _limit) {
_hasMore = false; // 如果返回数据少于一页,说明到底了
}
} catch (e) {
_errorMessage = e.toString();
} finally {
_isLoading = false;
notifyListeners();
}
}
// 上拉加载:追加数据
Future<void> loadMore() async {
if (!_hasMore) return; // 如果没有更多,直接返回
try {
// 计算偏移量:当前页数 * 每页数量
final newPosts = await _apiService.fetchPosts(start: _page * _limit, limit: _limit);
if (newPosts.isEmpty) {
_hasMore = false;
} else {
_posts.addAll(newPosts); // 关键:是 addAll 追加,不是覆盖
_page++;
if (newPosts.length < _limit) {
_hasMore = false;
}
}
} catch (e) {
_errorMessage = e.toString();
}
notifyListeners();
}
}
3. UI 层:集成 EasyRefresh
最后,我们在 UI 层将普通的 ListView 包裹在 EasyRefresh 组件中。
修改文件:lib/pages/post_list_page.dart
import 'package:easy_refresh/easy_refresh.dart';
// ... State 类中
late EasyRefreshController _controller;
void initState() {
super.initState();
// 初始化控制器
_controller = EasyRefreshController(
controlFinishRefresh: true, // 由代码控制刷新结束
controlFinishLoad: true, // 由代码控制加载结束
);
}
Widget build(BuildContext context) {
return Scaffold(
// ... AppBar
body: Consumer<PostProvider>(
builder: (context, provider, child) {
// ... 错误处理逻辑
return EasyRefresh(
controller: _controller,
header: const ClassicHeader(), // 经典的下拉刷新样式
footer: const ClassicFooter(), // 经典的上拉加载样式
// 下拉刷新回调
onRefresh: () async {
await provider.refresh();
if (!mounted) return;
_controller.finishRefresh(); // 告诉控件刷新完成了
_controller.resetFooter(); // 重置底部的“没有更多”状态
},
// 上拉加载回调
onLoad: () async {
if (!provider.hasMore) {
_controller.finishLoad(IndicatorResult.noMore); // 告诉控件没数据了
return;
}
await provider.loadMore();
if (!mounted) return;
// 根据是否有更多数据,返回不同的结果状态
_controller.finishLoad(
provider.hasMore ? IndicatorResult.success : IndicatorResult.noMore
);
},
child: ListView.builder(
itemCount: provider.posts.length,
itemBuilder: (context, index) {
// ... 列表项渲染逻辑
},
),
);
},
),
);
}
三、 运行效果与体验优化
1. 运行效果
编译并在鸿蒙真机上运行应用:
- 下拉:顶部会出现“下拉刷新” -> “释放刷新” -> “加载中”的状态变化,数据会重新获取。
- 上拉:滑动到底部时,会自动触发加载更多,新的 10 条数据会平滑地追加到列表尾部。
- 到底:当所有数据加载完毕(JSONPlaceholder 共 100 条),底部会显示“没有更多数据”。



2. 为什么不用 RefreshIndicator?
Flutter 自带的 RefreshIndicator 只能处理下拉刷新,对于上拉加载更多(Infinite Scroll),需要手动监听 ScrollController 的滚动位置,代码量大且容易出错(如抖动、重复请求)。EasyRefresh 封装了手势处理和状态机,极大地简化了开发流程。
四、 总结
虽然看似只是增加了一个交互功能,但其背后体现了移动端开发中至关重要的性能优化思想——分页加载。
通过本次实战掌握了:
- 后端分页接口的对接:理解
start/limit或page/size的参数意义。 - 状态管理的进阶:区分“刷新(重置)”与“加载(追加)”的数据流向。
- 第三方组件的鸿蒙适配:验证了
easy_refresh在 OpenHarmony 上的完美运行。
接下来的任务,我们将继续完善应用,探索更复杂的界面布局与原生交互。
欢迎加入与关注
-
AtomGit 主页:
https://atomgit.com/Betelgeuse76 -
开源鸿蒙跨平台开发者社区:
https://openharmonycrossplatform.csdn.net
更多推荐




所有评论(0)