【开源鸿蒙跨平台开发先锋训练营】Flutter实现鸿蒙多个标签功能并完善模块
·
基于前面的贴着,接着完善下鸿蒙项目的开发,完善每个选项卡对应页面的完整实现。
文章目录
一、项目概述
这是一款以作品流为核心的 鸿蒙 应用,采用底部四宫格导航:首页、发现、消息、我的。各页面功能独立,通过统一的作品数据源与作品详情页形成协同。
| 特性 | 说明 |
|---|---|
| 技术栈 | Flutter,Dio 网络请求,Material 3 |
| 数据来源 | 远程 JS 资源解析为作品列表与图片 URL |
| 展示形态 | 首页竖滑流 + 发现网格搜索 + 消息会话 + 个人中心 |
| 刘海屏UI | 对刘海屏顶部的处理 |
二、功能模块
2.1 首页(Home)
在这前面文章中已经学习并完成了首页全部的逻辑 【开源鸿蒙跨平台开发先锋训练营】Flutter实现列表上拉加载和下拉刷新
2.2 发现(Search)

入口:底部导航「发现」。
布局
- 顶部 AppBar 内嵌搜索框:圆角、占位文案「搜索作品、风格、作者、标签」、前缀搜索图标;右侧刷新按钮;空出刘海屏空间,不布局内容,保证内容显示的完整性。
- 主体为两列网格作品卡片;空态/错误态居中提示。
数据与筛选
- 与首页相同数据源:
ApiService+DataParser.parseWorksAndImages()获取作品列表。 - 搜索框实时过滤:关键词匹配作品标题、风格(style)、场景(scene)、作者名、标签(tags),不区分大小写;清空搜索则恢复全部作品。
// 搜索关键词实时过滤
void _onSearchChanged(String kw) {
final keyword = kw.trim().toLowerCase();
setState(() {
_filteredWorks = keyword.isEmpty ? _allWorks :
_allWorks.where((w) =>
w.title.toLowerCase().contains(keyword) ||
w.style.toLowerCase().contains(keyword) ||
w.scene.toLowerCase().contains(keyword) ||
w.author.name.toLowerCase().contains(keyword) ||
w.tags.any((t) => t.toLowerCase().contains(keyword))
).toList();
});
}
// 卡片点击查看详情
void _onCardTap(WorkModel work) {
Navigator.push(context, MaterialPageRoute(
builder: (_) => WorkDetailPage(work: work, initialIndex: 0)));
}

交互
- 点击卡片:进入作品详情页
WorkDetailPage(work, 0),传入该作品与initialIndex: 0。 - 下拉刷新:重新请求并更新列表。
- 参考:
lib/pages/search_page.dart
依赖
lib/pages/search_page.dartlib/pages/work_detail_page.dart、lib/models/work_model.dart、lib/api/api_service.dart、lib/utils/data_parser.dart、lib/widgets/network_image_widget.dart
2.3 消息(Messages)

入口:底部导航「消息」。
布局
- 标题「消息」,右侧可放置「添加」等入口。
- 会话列表:每项展示头像、会话名称、最后一条消息预览、时间、未读数角标;无会话时显示空态(图标 + 文案「暂无消息」)。
数据
- 使用
ConversationModel(id、name、avatarUrl、lastMessage、lastTime、unreadCount)与MessageModel(id、content、isMe、time)。 - 当前为本地模拟会话与消息,无真实后端。
// 聊天页发送消息回调
void _onMessageSent(String content) {
setState(() {
_conversation.lastMessage = content;
_conversation.lastTime = DateTime.now();
widget.onUpdated(_conversation); // 上层列表刷新未读与内容
});
}
交互
- 点击会话:进入聊天详情页(
ChatDetailPage)。 - 聊天详情页:
- 顶部标题为会话名称。
- 中部为消息列表(气泡:己方右对齐、对方左对齐,带时间)。
- 底部输入框 + 发送按钮;发送后本地追加一条己方消息,并回调
onMessageSent(content)更新会话列表的「最后一条消息」与时间,形成闭环。
依赖
lib/pages/messages_page.dartlib/models/message_model.dart
2.4 我的(Profile)

入口:底部导航「我的」。
布局
- 标题「我的」,右侧设置图标(可进入设置页)。
- 用户信息区:头像、昵称、获赞 / 收藏统计(基于当前拉取作品的 likes/saves 汇总,示例逻辑)。
- 功能列表:历史记录、设置、退出登录;每项带图标与副标题,右侧箭头。
数据
- 用户信息为占位(如「示例用户」)。
- 历史记录:复用
ApiService+DataParser拉取作品,取前若干条(如 6 条)作为「最近浏览」展示;统计数为作品维度汇总。
交互
- 历史记录:进入历史记录页,列表展示作品缩略图、标题、作者;点击某项 → 进入作品详情页。
- 设置:进入设置子页(通知、隐私、清除缓存等入口,具体逻辑可后续实现)。
- 退出登录:弹出确认对话框;确认后执行退出逻辑(当前为示例提示)。
// 获取最近浏览历史
List<WorkModel> getRecentHistory(int n) {
return _allWorks.take(n).toList();
}
// 退出登录操作
void _onLogout() async {
final ok = await showDialog(...);
if (ok == true) {
// 执行登出逻辑
}
}
依赖
lib/pages/profile_page.dartlib/pages/work_detail_page.dart、lib/models/work_model.dart、lib/api/api_service.dart、lib/utils/data_parser.dart
2.5 作品详情页(WorkDetail)
入口:首页(点击大图)、发现(点击卡片)、我的(历史记录点击)。
布局
- 全屏黑色背景;竖向 PageView 展示该作品多张图片,左右/上下滑动切换(与首页一致为竖向)。
- 顶部:返回按钮 + 当前张数 / 总张数。
- 底部渐变条:作品标题、风格/场景标签、作者头像与名称、平台、点赞数/收藏数。

数据
- 入参:
WorkModel work、int initialIndex(首屏显示的图片索引)。
// 切换图片更新索引
void _onPageChanged(int idx) {
setState(() {
_currentIndex = idx;
});
}
// 返回上一层
void _onBack() {
Navigator.pop(context);
}
交互
- 返回:关闭当前页回到上一页(首页/发现/我的或历史列表)。
依赖
lib/pages/work_detail_page.dartlib/models/work_model.dart、lib/widgets/network_image_widget.dart
三、数据模型与接口
3.1 作品与作者(WorkModel / AuthorModel)
- WorkModel:id、title、images(图片 URL 列表)、style、scene、tags、author、likes、saves、votes、rank、isEditorPick、createdAt。
- AuthorModel:id、name、avatar、platform、platformUrl。
- 定义于
lib/models/work_model.dart,支持fromJson/toJson。
3.2 消息与会话(MessageModel / ConversationModel)
- ConversationModel:id、name、avatarUrl、lastMessage、lastTime、unreadCount。
- MessageModel:id、content、isMe、time。
- 定义于
lib/models/message_model.dart,当前仅前端使用,无持久化。
3.3 数据获取与解析
- ApiService(
lib/api/api_service.dart):单例,Dio 封装;getMockData()请求指定 JS 资源并返回原始数据(字符串或已解析 JSON)。 - DataParser(
lib/utils/data_parser.dart):从 JS 内容中提取 JSON 数组,解析为WorkModel列表,并汇总所有图片 URL;对外主要使用parseWorksAndImages(jsContent),返回{ 'works': List<WorkModel>, 'images': List<String> }。
3.4 图片加载
- NetworkImageWidget(
lib/widgets/network_image_widget.dart):使用 Dio 以字节方式下载图片并展示,带加载中/失败态,便于在目标平台上稳定展示网络图。
四、页面与导航结构
RootPage(底部导航)
├── 首页 → HomePage
│ └── 点击大图 → WorkDetailPage(work, index)
├── 发现 → SearchPage
│ └── 点击卡片 → WorkDetailPage(work, 0)
├── 消息 → MessagesPage
│ └── 点击会话 → ChatDetailPage(conversation)
└── 我的 → ProfilePage
├── 历史记录 → _HistoryPage → 点击项 → WorkDetailPage(work)
├── 设置 → 设置子页
└── 退出登录 → 确认对话框
- 首页、发现、我的共用同一作品数据源与 WorkDetailPage,保证从任意入口进入的作品详情体验一致。
- 消息模块独立,通过会话列表与聊天详情的回调保持「最后一条消息」等状态同步。
五、技术实现要点
| 要点 | 说明 |
|---|---|
| 首页分页 | 维护 _images 全量与 _displayImages 当前页,滑动接近末尾触发 _loadMore()。 |
| 首页→详情映射 | 根据当前图片 URL 在 _works 中查找包含该 URL 的作品及在作品内的索引,再打开 WorkDetailPage。 |
| 发现搜索 | 搜索框 onChanged 中按关键词过滤 _allWorks 得到 _filteredWorks,网格绑定 _filteredWorks。 |
| 消息闭环 | ChatDetailPage 发送消息时调用 onMessageSent(content),MessagesPage 更新对应会话的 lastMessage、lastTime。 |
| 我的历史 | ProfilePage 初始化或刷新时请求作品数据,取前几条作为历史列表;历史页点击跳转 WorkDetailPage。 |
| 统一图片组件 | 列表/详情中的网络图均使用 NetworkImageWidget,保证加载与错误表现一致。 |
结束语
感谢阅读本帖,如对贴中内容有意见和建议的,欢迎与我联系交流,也欢迎加入开源鸿蒙跨平台社区:
https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)