【Flutter for OpenHarmony】训练营 DAY4-6:全栈实战——Node.js 后端部署与双向刷新列表
本阶段将带你跨越“单机应用”,实现与服务器的数据交互:
-
Mock 服务:使用 Node.js 搭建支持分页逻辑的 RESTful API。
-
高效列表:集成
pull_to_refresh实现下拉刷新与触底加载。 -
联调避坑:解决鸿蒙真机环境下局域网访问与权限配置问题。
一、 后端基座:Node.js 分页接口搭建
在开发初期,一个可控的本地后端能极大提升联调效率。
1. 服务初始化
创建 local_server 目录,安装核心依赖:
npm init -y
npm install express cors
2. 核心逻辑 (server.js)
这里增加了一个简单的数据生成器,确保有足够的分页测试数据。
JavaScript
const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(express.json());
// 生成 50 条测试数据
const allMemos = Array.from({ length: 50 }, (v, k) => ({
id: k + 1,
title: `备忘录事项 ${k + 1}`,
content: `这是来自 Node.js 后端的测试数据内容,编号为 ${k + 1}`,
time: new Date().toLocaleString()
}));
app.get('/api/memos', (req, res) => {
const page = parseInt(req.query.page) || 1;
const pageSize = parseInt(req.query.pageSize) || 10;
const startIndex = (page - 1) * pageSize;
const results = allMemos.slice(startIndex, startIndex + pageSize);
res.json({
code: 200,
data: results,
total: allMemos.length,
totalPages: Math.ceil(allMemos.length / pageSize)
});
});
app.listen(3000, '0.0.0.0', () => {
console.log('✅ 服务已启动: http://<你的电脑IP>:3000');
});
二、 前端攻坚:实现列表双向刷新
1. 数据模型定义
在 Flutter 中,先定义好对应的 Memo 模型,便于类型安全。
class Memo {
final int id;
final String title;
final String content;
Memo.fromJson(Map<String, dynamic> json)
: id = json['id'],
title = json['title'],
content = json['content'];
}
2. 状态管理与刷新控制
使用 SmartRefresher 是目前鸿蒙适配较好的选择。
// 核心刷新函数
Future<void> _onLoading({bool isRefresh = false}) async {
if (isRefresh) _currentPage = 1;
try {
final response = await http.get(Uri.parse("$baseUrl?page=$_currentPage&pageSize=10"));
if (response.statusCode == 200) {
final data = json.decode(response.body);
List<Memo> fetchedMemos = (data['data'] as List).map((i) => Memo.fromJson(i)).toList();
setState(() {
if (isRefresh) {
_memos = fetchedMemos;
_refreshController.refreshCompleted();
} else {
_memos.addAll(fetchedMemos);
_refreshController.loadComplete();
}
});
if (fetchedMemos.length < 10) _refreshController.loadNoData();
_currentPage++;
}
} catch (e) {
isRefresh ? _refreshController.refreshFailed() : _refreshController.loadFailed();
}
}
三、 鸿蒙真机联调关键配置 (必看)
很多开发者在这一步会遇到 Network Error,请务必检查以下三点:
1. 申请网络权限
打开 ohos/entry/src/main/module.json5,确保包含权限声明:
代码段
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.GET_NETWORK_INFO" }
]
2. 确定 BaseURL
-
模拟器:可以使用
10.0.2.2或电脑 IP。 -
真机:严禁使用 localhost。必须使用电脑的局域网 IP(如
192.168.x.x),且手机与电脑必须在同一 Wi-Fi 下。
3. 关闭防火墙
如果 IP 正确但仍无法访问,请尝试关闭电脑防火墙,或放行 3000 端口。
四、 成果演示
| 下拉刷新 (Pull) | 上拉加载 (Loading) | 数据耗尽 (End) |
| 头部显示水滴/圆圈,列表重置 | 底部加载动画,数据无缝拼接 | 底部显示“没有更多数据” |
当然可以!针对 DAY 4-6 进阶课程中关于“Flutter 与后端数据交互及分页逻辑”的核心知识点,我为你准备了一套更具鸿蒙特色的 UI 样式和技术升级建议。
以下是针对你提到的 2. 提供一套针对鸿蒙风格(HarmonyOS Design)定制的列表 UI 样式代码 的详细实现:
🎨 鸿蒙风格(HarmonyOS Style)列表 UI 实现
在鸿蒙生态中,视觉体验强调“轻盈、有活力、秩序感”。我们可以通过修改 ListView 的 Item 样式,使其更符合 HarmonyOS 设计规范:
-
圆角卡片:使用
8px或12px的大圆角。 -
层级阴影:微弱的投影,增加悬浮感。
-
字体排版:标题与正文有明显的字号和颜色对比。
1. 鸿蒙风格卡片组件 (MemoCard)
Dart
import 'package:flutter/material.dart';
class MemoCard extends StatelessWidget {
final String title;
final String content;
final String time;
const MemoCard({required this.title, required this.content, required this.time});
@override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16), // 鸿蒙典型大圆角
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Color(0xFF1A1A1A), // 深色文字
),
),
const Icon(Icons.arrow_forward_ios, size: 14, color: Colors.grey),
],
),
const SizedBox(height: 8),
Text(
content,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: 14,
color: Colors.black.withOpacity(0.6),
height: 1.5,
),
),
const SizedBox(height: 12),
Divider(color: Colors.grey.withOpacity(0.1)),
const SizedBox(height: 4),
Text(
time,
style: const TextStyle(fontSize: 12, color: Colors.grey),
),
],
),
);
}
}
🏗️ 分页请求逻辑示意图
为了帮你理清 _onRefresh 和 _onLoading 在整个生命周期中的作用,这张图展示了数据流转过程:
🛠️ 性能与体验优化技巧
在实现双向刷新时,还有几个提升“专业感”的小细节:
A. 骨架屏 (Skeleton Screen)
当第一次加载数据或下拉刷新时,不要只显示一个转圈圈,可以使用 shimmer 插件实现骨架屏,提升用户感知的加载速度。
B. 空状态处理
当搜索无结果或服务器没数据时,返回一个优雅的空状态图:
Dart
child: _memos.isEmpty
? Center(child: Column(children: [Image.asset('assets/empty.png'), Text("暂无备忘录")]))
: ListView.builder(...)
C. 鸿蒙系统特有的网络配置 (Reminder)
在 ohos/entry/src/main/module.json5 中,除了权限,如果你使用的是 HTTP 而非 HTTPS,可能需要配置:
代码段
"metadata": [
{
"name": "enable_manifest_network_config",
"value": "true"
}
]
并配置 network_config.json 允许明文传输(虽然 Flutter 引擎通常内部处理,但在原生混合开发时这是个常见坑)。
五、 总结与作业
通过这三天的进阶实战,我们打通了本地全栈开发流程。这不仅是关于一个列表的刷新,更是掌握了 Flutter 与鸿蒙原生系统之间网络权限调用的深度配合。
更多推荐

所有评论(0)