01. Flutter 鸿蒙实战 01:会说话的相册项目架构拆解

从目录、依赖、业务模块和鸿蒙工程开始,先看清一个完整 App 的骨架。
项目定位
talking_album 不是一个只展示页面的 Flutter Demo,而是一个已经接入后端、登录态、图片上传、故事生成、语音播放、视频播放和鸿蒙打包链路的客户端。pubspec.yaml 里的描述是"会说话的相册 Flutter HarmonyOS 客户端",项目目标也很清楚:把 Web 手机端的"会说话的相册"迁移到 Flutter,并让它可以运行在 HarmonyOS/OpenHarmony 工程里。
项目的入口非常轻,lib/main.dart 只负责启动应用:
dart
import 'package:flutter/material.dart';
import 'app/app.dart';
void main() {
runApp(const TalkingAlbumApp());
}
真正的应用初始化放在 lib/app/app.dart。这里创建 ApiClient、AuthController,再通过 AuthScope 注入到整棵组件树。这样的好处是业务页面不用到处 new 网络客户端,也不用把 token 状态层层传递。
dart
class _TalkingAlbumAppState extends State<TalkingAlbumApp> {
late final ApiClient _apiClient;
late final AuthController _authController;
@override
void initState() {
super.initState();
_apiClient = ApiClient();
_authController = AuthController(
apiClient: _apiClient,
sessionStore: SharedPreferencesSessionStore(),
);
_authController.initialize();
}
}
目录分层
项目目录按"应用入口、通用能力、业务功能、鸿蒙宿主"分层:
text
lib/
├─ app/ 应用入口、路由表
├─ core/
│ ├─ config/ API 地址等配置
│ ├─ network/ ApiClient、错误转换
│ ├─ speech/ 本地讲故事通道
│ ├─ theme/ 颜色和主题
│ └─ utils/ JSON 辅助
└─ features/
├─ auth/ 登录、注册、会话
├─ home/ 首页
├─ upload/ 照片上传
├─ stories/ 故事集、详情、语音、视频
├─ map/ 家族地图
├─ anniversary/ 纪念日
├─ family/ 家人列表
├─ notifications/ 通知中心
├─ profile/ 个人资料
└─ shell/ 顶部栏和底部导航
ohos/ 是 Flutter 鸿蒙宿主工程,不是临时目录。里面包含 EntryAbility.ets、GeneratedPluginRegistrant.ets、module.json5、资源文件和 HAP 构建产物。项目能跑在鸿蒙上,关键就在这套宿主工程和 Flutter OpenHarmony 工具链的配合。
鸿蒙侧工程
ohos/entry/src/main/ets/entryability/EntryAbility.ets 继承 FlutterAbility,并注册 Flutter 插件:
ts
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
export default class EntryAbility extends FlutterAbility {
configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
}
}
插件注册文件里能看到项目实际用到的鸿蒙适配插件:
ts
flutterEngine.getPlugins()?.add(new AudioplayersPlugin());
flutterEngine.getPlugins()?.add(new ImagePickerPlugin());
flutterEngine.getPlugins()?.add(new SharedPreferencesPlugin());
flutterEngine.getPlugins()?.add(new VideoPlayerPlugin());
这说明项目并不是只用 Flutter 画 UI,还用了鸿蒙侧插件能力:本地存储、图片选择、音频播放和视频播放都要靠 OpenHarmony 适配包。
依赖选择
pubspec.yaml 中最关键的依赖是这些:
yaml
shared_preferences_ohos:
git:
url: https://gitcode.com/openharmony-tpc/flutter_packages.git
path: packages/shared_preferences/shared_preferences_ohos
image_picker:
git:
url: https://gitcode.com/openharmony-tpc/flutter_packages.git
path: packages/image_picker/image_picker
audioplayers:
git:
url: https://gitee.com/openharmony-sig/flutter_audioplayers.git
ref: br_audioplayers-v6.1.0_ohos
path: packages/audioplayers
video_player:
git:
url: https://gitcode.com/openharmony-tpc/flutter_packages.git
ref: br_video_player-v2.9.2_ohos
path: packages/video_player/video_player
这类依赖不能随便替换成 pub.dev 上的普通版本。普通 Flutter 插件未必带 ohos 实现,代码能在 Android 跑,不代表能在鸿蒙工程里构建。
配图安排

完成这层结构梳理后,后面的登录会话、网络层、上传、故事、音频、视频、地图和 HAP 构建都能在同一套工程骨架里找到对应位置。
扩展拆解
进一步拆解:为什么这个架构适合继续扩展
这个项目的架构不是为了"看起来分层",而是为了后续功能继续增长。app 层只做应用装配,core 层放网络、主题、配置、语音通道,features 按业务拆分。这样新增功能时,开发者能快速判断代码应该放哪里:如果是业务页面,进入 features;如果是所有页面都要用的能力,进入 core;如果是启动、登录拦截、主题、本地化,进入 app。
例如"家人列表"不是写在首页里,而是单独放到 features/family。首页只提供入口,真正的列表、邀请、权限、管理菜单都在自己的模块内。这个边界清楚后,首页不会随着功能增加变成一个巨大的文件。
text
features/home 负责首页展示和入口跳转
features/family 负责家人列表、邀请和管理
features/stories 负责故事列表、详情、音频、视频
features/upload 负责照片选择、上传和上传列表管理
另一个关键点是 ohos 工程已经进入项目结构。Flutter 代码负责页面和业务逻辑,但鸿蒙侧仍然要处理 EntryAbility、权限、插件注册和 HAP 构建。写 Flutter 鸿蒙项目时,不能只看 lib,也要把 ohos 当作正式源码的一部分。
管理
另一个关键点是 `ohos` 工程已经进入项目结构。Flutter 代码负责页面和业务逻辑,但鸿蒙侧仍然要处理 EntryAbility、权限、插件注册和 HAP 构建。写 Flutter 鸿蒙项目时,不能只看 `lib`,也要把 `ohos` 当作正式源码的一部分。