Flutter for OpenHarmony (四)网络请求二部曲之上拉加载、下拉刷新及数据加载提示
我真傻,真的!我单知道…之前两篇,我直接把 git ssh 地址当做 http 链接贴上去了,今天才发现。现在都已经修复了。本次上拉和下拉使用的是pull_to_refresh库,使用上比较简单,基本上没有因为配置的问题查很久的文档,在鸿蒙的虚拟机上演示也是很丝滑。遇到的问题也是都是一些细节没有仔细导致的,大家练手的时候注意前后端的数据解构配合哦。
题记
上一篇实现了flutter → OH模拟器 →服务器 请求的一个完整的例子。但是比较简单,只是实现了基本的请求功能,但是我们做app 的话,上拉加载你得有吧,下拉刷新你也得有吧。请求过程的中的友好交互你得做吧。
哇呀呀呀 , 开干。
思路
- 引入pull_to_refresh库来实现app 的上拉和下拉功能
- 后端增加大量的 mock 数据,以支撑多次上拉效果。
- 为了让交互更加的丝滑,加入请求骨架屏(替换之前的 loading)、加载失败、无更多数据、空数据等状态。
实现上拉和下拉功能
安装 pull_to_refresh 库
为什么选择pull_to_refresh库呢?
“如果说原生组件是‘能用’,那么 pull_to_refresh 就是‘好用且全能’——它把繁杂的状态判断封装成了简单的回调,让开发者能专注于业务逻辑而非滚动算法。”
作为 Flutter 社区的老牌插件,它的兼容性极强,支持所有的 ScrollView(ListView, GridView, CustomScrollView),是目前最省心的方案。
添加下拉效果
- 添加 Flutter 下拉效果的代码
这里的下拉以及上拉的效果都是使用SmartRefresher来包裹其中的展示数据,也即是包裹整个页面。
body: Scaffold(
body: SmartRefresher(
// 是否允许下拉刷新
enablePullDown: true,
// 是否允许上拉加载更多
enablePullUp: true,
// 设定下拉刷新的头部样式(这里使用的是水滴效果)
// ; header: WaterDropHeader(),
header: classicIndicator(),
footer: footerIndicator(),
// 控制器,用于手动触发刷新或停止刷新动画
controller: _refreshController,
// 下拉触发的回调函数
onRefresh: _onRefresh,
// 上拉触发的回调函数
onLoading: _onLoading,
child: _buildBody(),
),
),
重点是,这里我们要注册要给控制器给到 SmartRefresher 中 controller 选项。
final RefreshController _refreshController = RefreshController(
initialRefresh: false, // 之前我们已经在页面中添加了 initState 这里就不改了
);
- 修改后端接口
原来的接口,如果只是用来下拉更新的话是没问题的,但是涉及到了上拉加载,这就涉及到了分页,我们对原有的数据进行改造一番。并加两个一个 5s 的延迟,毕竟是本地的环境,加载太快了 看不到 loading 的效果。
fastify.post('/load', async function handler(request, reply) {
const page = request.body.page ?? 1;
const pagesize = request.body.pagesize ?? 5;
const data = [
{ hello: 'world' },
{ hello: 'harmony' },
{ hello: 'sqllite' },
{ hello: 'ArkUI' },
{ hello: 'ArkTS' },
{ hello: 'DevEco Studio' },
{ hello: 'Ability' },
{ hello: 'Service Extension Ability' },
{ hello: 'Distributed Soft Bus' },
{ hello: 'Atomic Service' },
{ hello: 'HarmonyOS SDK' },
{ hello: 'OpenHarmony' },
{ hello: 'HAP' }
]
await new Promise(resolve => setTimeout(resolve, 5000));
return {
code: 200,
message: 'success',
data: {
list: data.slice((page - 1) * pagesize, page * pagesize),
total: data.length,
page,
pagesize
}
};
})
- 进行下拉刷新的步骤
首先我们要对上面的下拉效果代码进行补全,我这里只记录一下classicIndicator(), footerIndicator(), 这两个函数。
Widget classicIndicator() {
return const ClassicHeader(
idleText: '下拉刷新',
releaseText: '释放刷新',
refreshingText: '正在刷新...',
completeText: '刷新完成',
failedText: '刷新失败',
);
}
Widget footerIndicator() {
return CustomFooter(
builder: (context, mode) {
Widget body;
// 状态 1:闲置状态,等待用户上拉
if (mode == LoadStatus.idle) {
body = Text("上拉加载更多");
}
// 状态 2:正在加载数据中
else if (mode == LoadStatus.loading) {
// body = CupertinoActivityIndicator(); // 显示 iOS 风格的加载菊花
body = Text("正在加载...");
}
// 状态 3:加载失败
else if (mode == LoadStatus.failed) {
body = Text("加载失败!点击重试");
}
// 状态 4:上拉位移足够,释放即可开始加载
else if (mode == LoadStatus.canLoading) {
body = Text("释放开始加载");
}
// 状态 5:没有更多数据了
else {
body = Text("没有更多数据了");
}
debugPrint('🚀 [Refresh Status]: $mode - $body');
// 返回页脚的容器,固定高度 55 像素并居中显示内容
return SizedBox(height: 55.0, child: Center(child: body));
},
);
}
这里我注意到两个函数,是不一样的。上拉的时候还要手动控制?这里显然不合理,查了下源码,发现有对应的 ClassicFooter 函数,也就是说不用手动去控制了(可以偷懒了)。
return ClassicFooter(
// 加载状态的文字
loadingText: "正在努力加载中...",
// 准备加载时的文字
canLoadingText: "松开即可加载更多",
// 闲置状态(上拉前)的文字
idleText: "上拉加载更多",
// 没有更多数据时的文字
noDataText: "到底啦,没有更多数据了",
// 加载失败时的文字
failedText: "加载失败,请重试",
height: 60.0,
);
哦哦哦 最重要的是,我们接口已经变化了,这时候要调整一下请求的接口函数,添加,page 以及 pagesize。
Future<void> loadData({int page = 1, int pagesize = 5}) => _handleRequest(
dio.post('$HOST/load', data: {'page': page, 'pagesize': pagesize}),
isAppend: page > 1,
);
查看效果以及解决问题
我们请求一下看看效果,好像出了点问题。忘记处理返回类型了。
// 原来的类型处理
final response = await request;
setState(() {
// 假设返回的是一个 List,如果不是,需要根据具体 JSON 结构解析
_data = response.data as List<dynamic>;
_isLoading = false;
});
// 调整之后
// 服务端返回结构为 { code: 200, data: { list: [], total: 13, ... } }
final Map<String, dynamic> responseData =
response.data as Map<String, dynamic>;
final List<dynamic> result =
responseData['data']['list'] as List<dynamic>;
这里再次请求之后又出现了一个问题报错。
提示 404 了,我们看看后端日志报了什么错。
哈哈 干了件蠢事,想着后面会继续用到我们的 HOST,就单独提取出来到一个配置文件里。但是拼接的时候忘记还多了一个 / ,这可就要了命了。
// 这里采用这样的写法,多少一个/无所谓了,即使你习惯前面加个 /
Uri.parse(HOST).resolve('/load').toString(),
下拉刷新这次真的ok了

这里还出了个小岔子,多了个大 loading 框框,还需要我们把之前加的全局 loading 框框给去掉。
OK 到这里下拉刷新的效果就 OK 了。
上拉加载也OK了
解决了之前的全局 loading 情况,上拉加载的也好看了很多。
这里还有一个事情要注意,后端已经返回了 total 数据,需要我们判断一下,否则没有拉到底的文字提示。
void _onRefresh() async {
final success = await loadData();
if (success) {
_refreshController.refreshCompleted();
// 如果数据已经加载完,显示“到底啦”
if (_data.length >= _total) {
_refreshController.loadNoData();
} else {
_refreshController.resetNoData(); // 重置加载状态
}
} else {
_refreshController.refreshFailed();
}
}
到这里的话 网络请求已经全部结束了。具体的代码已经发到 AutoGit上,有 兴趣可以下来看看。
总结
我真傻,真的!我单知道…
之前两篇,我直接把 git ssh 地址当做 http 链接贴上去了,今天才发现。现在都已经修复了。
本次上拉和下拉使用的是pull_to_refresh库,使用上比较简单,基本上没有因为配置的问题查很久的文档,在鸿蒙的虚拟机上演示也是很丝滑。
遇到的问题也是都是一些细节没有仔细导致的,大家练手的时候注意前后端的数据解构配合哦。
欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)