Flutter+HarmonyOS跨端实战—第01篇:项目架构设计与技术选型
从零开始搭建一个支持三端的 Flutter 应用
前言
做跨平台开发这么多年,最怕的就是项目做到一半发现架构不合理,重构起来痛不欲生。这次做 CleanMark AI 项目,我花了整整两天时间做技术选型和架构设计,后面的开发过程证明这两天没白费。
这篇文章我会把整个决策过程分享给你,包括为什么选择某个技术,为什么放弃另一个技术,以及踩过的坑。
一、为什么选择 Flutter + HarmonyOS
1.1 业务需求分析
先说说项目背景。CleanMark AI 是一个 AI 去水印工具,核心功能是:
- 图片去水印(1积分/次)
- 视频去水印(5积分/次)
- 积分系统(看广告赚积分)
- 历史记录管理
关键需求:
- 需要同时支持 Android、iOS、HarmonyOS 三端
- UI 要求高(深色主题、渐变效果、动画)
- 需要调用原生能力(相机、相册、视频播放)
- 要集成华为广告 SDK

1.2 技术方案对比
我当时考虑了三种方案:
| 方案 | 优点 | 缺点 | 结论 |
|---|---|---|---|
| 原生开发 | 性能最好,体验最佳 | 三端代码量 3 倍,维护成本高 | ❌ 团队只有 2 个人,搞不定 |
| React Native | 生态成熟,社区活跃 | HarmonyOS 支持不完善 | ❌ 适配成本太高 |
| Flutter | 一套代码三端运行,性能接近原生 | HarmonyOS 需要自己适配插件 | ✅ 最终选择 |
为什么选 Flutter?
- 性能够用:60fps 的 UI 渲染,对于我们这种工具类应用完全够了
- 开发效率高:Hot Reload 真的能省很多时间
- HarmonyOS 官方支持:华为提供了 Flutter SDK for HarmonyOS
- 插件生态:虽然 HarmonyOS 插件少,但可以自己写
1.3 HarmonyOS 适配的挑战
选择 Flutter 不代表没有坑,HarmonyOS 适配主要有三个难点:
难点1:插件适配
Flutter 官方插件大多不支持 HarmonyOS,需要自己写平台代码。比如:
image_picker- 需要用 ArkTS 调用 HarmonyOS 的 Picker APIvideo_player- 需要封装 AVPlayershared_preferences- 需要用 Preferences API
难点2:ArkTS 语法限制
HarmonyOS NEXT 使用 ArkTS(TypeScript 的子集),有很多限制:
- 不支持
Function.bind() - 不支持对象解构
- 不支持 Spread 操作符
- 类型检查非常严格
难点3:调试困难
HarmonyOS 的错误信息不如 Android 清晰,经常遇到:
THREAD_BLOCK_6S: Main thread blocked for 6 seconds
这种错误,排查起来很费时间。
二、Clean Architecture 架构设计
2.1 为什么需要架构
很多人觉得小项目不需要架构,直接写就行。但我的经验是:没有架构的项目,后期维护成本是有架构的 3-5 倍。
CleanMark AI 虽然只有 12 个页面,但功能不简单:
- 用户认证
- 积分系统
- 图片/视频处理
- 历史记录
- 广告集成
如果不做好分层,代码很快就会变成一团乱麻。
2.2 Clean Architecture 三层结构
我采用了 Uncle Bob 的 Clean Architecture,简化成三层:
┌─────────────────────────────────────────┐
│ Presentation Layer │
│ (UI + State Management + Navigation) │
│ │
│ - Pages (页面) │
│ - Widgets (组件) │
│ - Providers (状态管理) │
└─────────────────────────────────────────┘
↓ 只能依赖
┌─────────────────────────────────────────┐
│ Domain Layer │
│ (Business Logic + Entities) │
│ │
│ - Entities (实体) │
│ - Use Cases (用例) │
│ - Repository Interfaces (仓库接口) │
└─────────────────────────────────────────┘
↓ 只能依赖
┌─────────────────────────────────────────┐
│ Data Layer │
│ (Data Sources + Repository Impl) │
│ │
│ - Models (数据模型) │
│ - Data Sources (数据源) │
│ - Repository Implementations (仓库实现)│
└─────────────────────────────────────────┘
核心原则:依赖倒置
- Presentation 层只能依赖 Domain 层
- Domain 层不能依赖任何层(纯 Dart,无 Flutter 依赖)
- Data 层实现 Domain 层定义的接口
2.3 项目目录结构
基于 Clean Architecture,我设计了这样的目录结构:
lib/
├── app/ # 应用层
│ ├── app.dart # MaterialApp 配置
│ ├── router.dart # 路由配置
│ ├── theme.dart # 主题配置
│ └── app_colors.dart # 颜色常量
│
├── core/ # 核心层(跨功能共享)
│ ├── constants/ # 常量
│ │ └── api_constants.dart # API 路径
│ ├── network/ # 网络层
│ │ └── api_client.dart # Dio 封装
│ ├── platform/ # 平台适配层
│ │ ├── image_picker_service.dart
│ │ └── storage_service.dart
│ ├── services/ # 共享服务
│ │ ├── inpaint_service.dart # 去水印服务
│ │ └── task_service.dart # 任务管理
│ └── utils/ # 工具类
│ ├── app_prefs.dart # 本地存储
│ ├── mask_generator.dart # 遮罩生成
│ └── video_utils.dart # 视频工具
│
├── features/ # 功能模块(按业务领域划分)
│ ├── auth/ # 认证模块
│ │ ├── auth_service.dart
│ │ ├── login_screen.dart
│ │ ├── user_model.dart
│ │ └── user_provider.dart
│ │
│ ├── home/ # 首页模块
│ │ └── home_screen.dart
│ │
│ ├── image/ # 图片处理模块
│ │ ├── image_upload_screen.dart
│ │ ├── image_comparison_screen.dart
│ │ └── widgets/
│ │ └── paint_canvas.dart
│ │
│ ├── video/ # 视频处理模块
│ │ ├── video_upload_screen.dart
│ │ └── video_result_screen.dart
│ │
│ ├── history/ # 历史记录模块
│ │ ├── history_list_screen.dart
│ │ ├── history_detail_screen.dart
│ │ ├── history_service.dart
│ │ ├── task_model.dart
│ │ └── widgets/
│ │ └── record_card.dart
│ │
│ ├── points/ # 积分模块
│ │ ├── points_history_screen.dart
│ │ ├── earn_points_screen.dart
│ │ └── ad_service.dart
│ │
│ └── onboarding/ # 引导页模块
│ └── onboarding_screen.dart
│
└── main.dart # 应用入口
为什么这样设计?
-
按功能划分,不按类型划分
- ❌ 不要:
lib/screens/,lib/widgets/,lib/models/ - ✅ 推荐:
lib/features/auth/,lib/features/home/
- ❌ 不要:
-
每个功能模块独立
- 模块内的文件只被模块内使用
- 跨模块共享的放到
core/或shared/
-
平台相关代码统一封装
- 所有 Platform Channel 调用都在
core/platform/中 - 业务代码不直接调用原生 API
- 所有 Platform Channel 调用都在
三、技术栈选型
3.1 状态管理:Riverpod
候选方案对比:
| 方案 | 优点 | 缺点 | 选择 |
|---|---|---|---|
| Provider | 简单易用,官方推荐 | 样板代码多,类型安全差 | ❌ |
| Bloc | 架构清晰,适合大型项目 | 学习曲线陡,代码量大 | ❌ |
| Riverpod | 类型安全,编译时检查,代码简洁 | 相对较新,社区小 | ✅ |
为什么选 Riverpod?
// Provider 的问题:需要 BuildContext
final user = Provider.of<User>(context);
// Riverpod 的优势:不需要 BuildContext
final user = ref.watch(userProvider);
Riverpod 的核心优势:
- 编译时类型检查:Provider 不存在会编译报错
- 不依赖 BuildContext:可以在任何地方使用
- 自动依赖管理:Provider 之间的依赖关系自动处理
- 测试友好:可以轻松 mock Provider
实际使用示例:
// 定义 Provider
final userProvider = StateNotifierProvider<UserNotifier, User?>((ref) {
return UserNotifier();
});
// 在 Widget 中使用
class HomeScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final user = ref.watch(userProvider);
return Text('积分: ${user?.credits ?? 0}');
}
}
3.2 路由管理:go_router
为什么不用 Navigator?
Flutter 自带的 Navigator 有几个问题:
- 路由配置分散,难以维护
- 不支持深链接
- 路由守卫实现复杂
go_router 的优势:
// 集中式路由配置
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const IndexScreen(),
),
GoRoute(
path: '/login',
builder: (context, state) => const LoginScreen(),
),
GoRoute(
path: '/main',
builder: (context, state) => const MainScreen(),
redirect: (context, state) {
// 路由守卫:未登录跳转到登录页
final isLoggedIn = /* 检查登录状态 */;
return isLoggedIn ? null : '/login';
},
),
],
);
// 页面跳转
context.go('/main');
context.push('/image-upload');
实际项目中的路由配置:
// lib/app/router.dart
final routerProvider = Provider<GoRouter>((ref) {
final authState = ref.watch(authStateProvider);
return GoRouter(
initialLocation: '/',
redirect: (context, state) {
final isLoggedIn = authState.isLoggedIn;
final isLoginRoute = state.matchedLocation == '/login';
// 未登录且不在登录页,跳转到登录页
if (!isLoggedIn && !isLoginRoute) {
return '/login';
}
// 已登录且在登录页,跳转到首页
if (isLoggedIn && isLoginRoute) {
return '/main';
}
return null;
},
routes: [
// ... 路由配置
],
);
});
四、开发环境搭建
4.1 Flutter SDK 安装
系统要求:
- macOS / Windows / Linux
- 磁盘空间:至少 2.8 GB
- Git 已安装
安装步骤:
# 1. 下载 Flutter SDK
git clone https://github.com/flutter/flutter.git -b stable
# 2. 配置环境变量(macOS/Linux)
export PATH="$PATH:`pwd`/flutter/bin"
# 3. 运行 flutter doctor 检查环境
flutter doctor
# 4. 安装 HarmonyOS 支持
flutter config --enable-ohos
常见问题:
# 问题1:Android licenses 未接受
flutter doctor --android-licenses
# 问题2:Xcode 未安装(macOS)
# 从 App Store 安装 Xcode
# 问题3:网络问题(国内)
# 配置镜像源
export PUB_HOSTED_URL=https://pub.flutter-io.cn
export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
4.2 DevEco Studio 安装(HarmonyOS 开发)
下载地址:
https://developer.huawei.com/consumer/cn/deveco-studio/
安装步骤:
- 下载 DevEco Studio 安装包
- 安装并启动
- 配置 HarmonyOS SDK
- 创建模拟器或连接真机
配置 Flutter 项目支持 HarmonyOS:
# 1. 创建 Flutter 项目
flutter create my_app
# 2. 添加 HarmonyOS 平台支持
cd my_app
flutter create --platforms=ohos .
# 3. 查看项目结构
ls -la
# 会看到新增的 ohos/ 目录
本篇小结
这篇文章我们完成了:
- ✅ 技术选型:Flutter + Riverpod + go_router
- ✅ 架构设计:Clean Architecture 三层结构
- ✅ 目录规划:按功能模块划分
- ✅ 环境搭建:Flutter SDK + DevEco Studio
下一篇我们会深入讲解路由配置和状态管理的实战应用。
思考题
- 为什么 Domain 层不能依赖 Flutter 框架?
- 如果你的项目只支持 Android 和 iOS,还需要 Clean Architecture 吗?
- Riverpod 和 Bloc 各适合什么场景?
欢迎在评论区分享你的想法!
下一篇预告:第02篇 - 路由与状态管理实战
更多推荐

所有评论(0)