Flutter 架构演进实战:从 MVC 到 Clean Architecture + Modularization 的大型项目重构指南

引言:当你的 Flutter 项目超过 10 万行代码

初期,一个简单的 main.dart + 几个页面就能满足需求。但随着业务增长,代码逐渐演变为:

  • 所有逻辑塞进 StatefulWidget;
  • 网络请求、状态管理、UI 混杂在一起;
  • 修改一处,多处意外崩溃;
  • 新成员入职两周仍不敢提交代码;
  • 构建时间超过 5 分钟。

这正是 架构缺失 的典型症状。许多团队误以为"Flutter 天生组件化",却忽略了分层、解耦、可测试性才是大型项目的生命线。

本文将带你完成一次 真实项目的架构演进 :从混乱的 MVC 式代码,逐步重构为 Clean Architecture + Modularization + Feature-Sliced Design 的现代化结构。我们将覆盖 目录组织、依赖注入、状态管理、模块边界、CI/CD 优化 等核心议题,并提供可直接复用的模板。

目标读者:负责 Flutter 项目架构、技术选型或大型应用维护的工程师与技术负责人。


一、为什么需要 Clean Architecture?

1.1 核心原则:关注点分离(SoC)

Clean Architecture 由 Robert C. Martin 提出,核心思想是:

  • 内层(Entities, Use Cases)不依赖外层(UI, Frameworks)
  • 依赖方向永远指向内层
  • 业务逻辑独立于框架、数据库、UI

1.2 Flutter 中的四层模型

层级 职责 技术栈
Presentation UI、用户交互、路由 Flutter Widgets, Riverpod, GoRouter
Domain 业务逻辑、实体、Use Case Pure Dart, Entities, Repositories(接口)
Data 数据源实现(网络、本地) Dio, Hive, Shared Preferences
Common 工具、扩展、常量 utils/, extensions/, constants/

关键 :Domain 层 零依赖 Flutter SDK,可独立测试与复用。


二、重构第一步:目录结构标准化

2.1 反面教材:按类型组织(Type-Based)

cs 复制代码
1lib/
2├── models/
3├── services/
4├── widgets/
5├── screens/
6└── main.dart

❌ 问题:修改一个功能需横跨多个目录,认知负荷高。

2.2 推荐方案:按特性组织(Feature-Sliced)

cs 复制代码
1lib/
2├── core/                  # 跨特性共享(DI、网络、主题)
3│   ├── di/
4│   ├── network/
5│   └── theme/
6├── features/
7│   ├── auth/              # 登录/注册
8│   │   ├── data/          # Auth 数据源
9│   │   ├── domain/        # Auth 业务逻辑
10│   │   └── presentation/  # 登录页面、状态
11│   └── profile/           # 个人资料
12└── main.dart

优势

  • 功能内聚,修改集中;
  • 新成员快速定位代码;
  • 支持模块懒加载。

三、依赖注入:解耦的核心引擎

3.1 为什么不用 GetIt?

虽然 get_it 简单,但在大型项目中存在:

  • 隐式依赖,难以追踪;
  • 无法按需初始化;
  • 测试时需手动重置。

3.2 推荐:Riverpod + AsyncNotifier

Dart 复制代码
1// features/auth/domain/use_cases/sign_in_use_case.dart
2class SignInUseCase {
3  final AuthRepository repository;
4  SignInUseCase(this.repository);
5
6  Future<User> execute(String email, String password) async {
7    return await repository.signIn(email, password);
8  }
9}
10
11// features/auth/data/repositories/auth_repository_impl.dart
12final authRepositoryProvider = Provider<AuthRepository>((ref) {
13  final dio = ref.watch(dioProvider);
14  return AuthRepositoryImpl(AuthApiClient(dio));
15});
16
17// features/auth/domain/use_cases/sign_in_use_case_provider.dart
18final signInUseCaseProvider = Provider<SignInUseCase>((ref) {
19  final repo = ref.watch(authRepositoryProvider);
20  return SignInUseCase(repo);
21});

优势

  • 依赖显式声明;
  • 自动生命周期管理;
  • 测试时可轻松 Override。

四、状态管理:从 setState 到 AsyncNotifier

4.1 反模式:巨型 StatefulWidget

Dart 复制代码
1class ProfilePage extends StatefulWidget {
2  @override
3  _ProfilePageState createState() => _ProfilePageState();
4}
5
6class _ProfilePageState extends State<ProfilePage> {
7  User? user;
8  bool loading = false;
9
10  void loadUser() async {
11    setState(() => loading = true);
12    final user = await api.getUser(); // 直接调用 API!
13    setState(() {
14      this.user = user;
15      loading = false;
16    });
17  }
18}

❌ 问题:逻辑与 UI 耦合,无法复用,难以测试。

4.2 正确姿势:AsyncNotifier + Use Case

Dart 复制代码
1// features/profile/presentation/notifiers/profile_notifier.dart
2@riverpod
3class ProfileNotifier extends _$ProfileNotifier {
4  @override
5  Future<User?> build() async => null;
6
7  Future<void> loadUser() async {
8    final useCase = ref.read(getUserUseCaseProvider);
9    state = const AsyncLoading();
10    state = await AsyncValue.guard(() => useCase.execute());
11  }
12}
13
14// UI 层
15class ProfilePage extends ConsumerWidget {
16  @override
17  Widget build(BuildContext context, WidgetRef ref) {
18    final userAsync = ref.watch(profileNotifierProvider);
19    return userAsync.when(
20      loading: () => CircularProgressIndicator(),
21      error: (err, _) => Text('Error'),
22      data: (user) => user != null ? UserProfile(user) : LoginButton(),
23    );
24  }
25}

效果

  • 业务逻辑移至 Use Case;
  • 状态可预测、可测试;
  • UI 仅负责渲染。

五、模块化(Modularization):拆分巨型 App

5.1 为什么需要模块化?

  • 编译速度慢(全量编译);
  • 团队并行开发冲突多;
  • 无法按需加载功能。

5.2 使用 flutter_modular 实现

(1)定义模块
Dart 复制代码
1// features/auth/auth_module.dart
2class AuthModule extends Module {
3  @override
4  List<Bind> get binds => [
5        Bind.singleton((i) => AuthRepositoryImpl(i())),
6        Bind.singleton((i) => SignInUseCase(i())),
7      ];
8
9  @override
10  List<ModularRoute> get routes => [
11        ChildRoute('/', child: (_, __) => const LoginPage()),
12        ChildRoute('/register', child: (_, __) => const RegisterPage()),
13      ];
14}
(2)主应用集成
Dart 复制代码
1// main.dart
2void main() {
3  runApp(ModularApp(
4    module: AppModule(),
5    child: const MyApp(),
6  ));
7}
8
9class MyApp extends StatelessWidget {
10  @override
11  Widget build(BuildContext context) {
12    return MaterialApp.router(
13      routeInformationParser: Modular.routeInformationParser,
14      routerDelegate: Modular.routerDelegate,
15    );
16  }
17}

优势

  • 模块自治(路由、依赖、生命周期);
  • 支持动态加载(未来);
  • 团队按模块分工。

六、测试策略:分层保障质量

层级 测试内容 工具
Domain Use Case 逻辑、Entity 方法 test
Data Repository 实现、API Client mocktail, http_mock_adapter
Presentation Widget 渲染、用户交互 flutter_test
Integration 跨模块流程 integration_test

覆盖率目标:Domain ≥ 90%,Data ≥ 80%,Presentation ≥ 70%。


七、CI/CD 优化:加速大型项目构建

7.1 问题:全量构建太慢

  • 每次 PR 都需编译整个 App;
  • 浪费 CI 资源。

7.2 解决方案:按模块缓存

cs 复制代码
1# .github/workflows/test.yml
2- name: Cache modules
3  uses: actions/cache@v4
4  with:
5    path: |
6      **/build/
7      **/.dart_tool/
8    key: ${{ runner.os }}-modules-${{ hashFiles('**/pubspec.lock') }}

7.3 增量测试

bash 复制代码
1# 仅测试变更模块
2changed_files=$(git diff --name-only HEAD~1)
3if echo "$changed_files" | grep -q "features/auth/"; then
4  flutter test test/features/auth/
5fi

八、演进路线图:分阶段重构

阶段 目标 周期
1. 拆分目录 按特性组织代码 1 周
2. 提取 Domain 将业务逻辑移出 UI 2 周
3. 引入 DI 替换硬编码依赖 1 周
4. 模块化 拆分为独立模块 3 周
5. 自动化 CI + 覆盖率门禁 持续

原则小步快跑,持续交付,不追求一步到位


结语:架构不是设计出来的,而是演进出来的

没有银弹架构,只有适合当前团队与业务的结构。本文提供的 Clean + Modular 方案,已在多个百万级用户 App 中验证有效。记住:好的架构让正确的事容易做,错误的事难以发生

行动清单

  1. 本周内将一个功能迁移到 feature 目录;
  2. 为核心 Use Case 编写单元测试;
  3. 在团队内推行目录规范;
  4. 监控构建时间,设定优化目标。

唯有持续演进,才能让 Flutter 项目在规模膨胀中依然保持敏捷与健壮。

欢迎大家加入[开源鸿蒙跨平台开发者社区](https://openharmonycrossplatform.csdn.net),一起共建开源鸿蒙跨平台生态。

相关推荐
程序员Ctrl喵18 小时前
异步编程:Event Loop 与 Isolate 的深层博弈
开发语言·flutter
前端不太难19 小时前
Flutter 如何设计可长期维护的模块边界?
flutter
小蜜蜂嗡嗡20 小时前
flutter列表中实现置顶动画
flutter
始持21 小时前
第十二讲 风格与主题统一
前端·flutter
始持21 小时前
第十一讲 界面导航与路由管理
flutter·vibecoding
始持21 小时前
第十三讲 异步操作与异步构建
前端·flutter
新镜21 小时前
【Flutter】 视频视频源横向、竖向问题
flutter
黄林晴1 天前
Compose Multiplatform 1.10 发布:统一 Preview、Navigation 3、Hot Reload 三箭齐发
android·flutter
Swift社区1 天前
Flutter 应该按功能拆,还是按技术层拆?
flutter
肠胃炎1 天前
树形选择器组件封装
前端·flutter