简单而系统地管理你的 Flutter 模块化结构
简介
随着我们的项目增长,模块化似乎势在必行,而我最近发现了一个比较新的Flutter模块化管理框架------Flutist。它是一个专为 Flutter 应用设计的强大项目管理框架,灵感来源于 iOS 开发生态中的 Tuist[1]。它为管理大型 Flutter 项目提供了一套结构化的方法,具备模块化架构、集中式依赖管理和代码生成能力。
为什么选择 Flutist?
模块化是大型 Flutter 项目的标准方案------独立构建、并行开发、测试隔离,优势显而易见。但随着模块数量增长,管理开销也随之攀升。Flutist 通过自动化消除了这些开销。
类型安全的依赖管理
当模块超过 10 个时,包版本不一致的问题极易出现。在 package.dart 中声明一次版本,flutist generate 会自动生成 flutist_gen.dart,所有模块都能通过 IDE 自动补全和类型检查安全地引用依赖。
dart
// 1. package.dart --- 只声明一次版本
Dependency(name: 'dio', version: '^5.3.0'),
Dependency(name: 'flutter_bloc', version: '^8.1.6'),
// 2. flutist_gen.dart --- 由 flutist generate 自动生成
Dependency get dio => dependencies.firstWhere((d) => d.name == 'dio');
Dependency get flutterBloc => dependencies.firstWhere((d) => d.name == 'flutter_bloc');
Module get authDomain => modules.firstWhere((m) => m.name == 'auth_domain');
// 3. project.dart --- 类型安全引用(IDE 自动补全 ✅)
Module(
name: 'auth_data',
dependencies: [package.dependencies.dio, package.dependencies.flutterBloc],
modules: [package.modules.authDomain],
),
集中式 pubspec.yaml 管理
每增加一个模块就多一个 pubspec.yaml。升级一个包的版本意味着要手动编辑每个引用它的文件。
Flutist 根据 project.dart 的声明自动同步所有 pubspec.yaml 文件。开发者只需编辑一个文件------project.dart。
bash
$ flutist generate
✓ pubspec.yaml synced: app, auth_domain, auth_data, auth_presentation,
product_interface, product_implementation ... (24 total)
✓ all architecture rules passed
✓ done (0.8s)
架构规则自动化
仅靠文档和代码审查很难持续保持架构规则的一致性。在开发压力下,domain 层最终会导入 http,或者一个功能模块直接引用了另一个功能模块的实现。这些违规很难被发现,等到发现时往往已经扩散开来。
Flutist 将架构规则转化为可执行代码 。在 strictMode: true(默认值)下,任何违规都会立即终止 generate。曾经只存在于文档中的原则,现在变成了构建关卡。
kotlin
$ flutist generate
✗ [B4] auth_domain → auth_data: 检测到反向依赖 --- domain 不应依赖 data
→ 请从 auth_domain 中移除 Dio 导入,仅声明 Repository 接口
✗ generate 已终止(strictMode: true)
即使是刚加入团队、对架构理解不深的新成员,在违反规则的那一刻也能获得清晰的反馈。架构违规不再依赖人工审查,工具会自动检查。
样板代码自动生成
模块化架构最大的痛点之一就是样板代码。每个新功能都需要创建 interface、implementation、testing、tests 和 example 包,每个包都有自己的 pubspec.yaml、lib/ 结构和 barrel 文件。
像 BLoC 这样的状态管理模式,每个功能都需要 event、state、BLoC、page 和 widget 文件。使用 flutist create 和 flutist scaffold,一条命令就能生成所有这些内容。
bash
# 以 micro 类型创建 todos 功能 --- 5 个包 + 完整结构自动化
$ flutist create --name todos --path features --options micro
✓ features/todos/todos_interface 已创建
✓ features/todos/todos_implementation 已创建
✓ features/todos/todos_testing 已创建
✓ features/todos/todos_tests 已创建
✓ features/todos/todos_example 已创建
# 使用 BLoC 脚手架生成所有文件
$ flutist scaffold --template bloc --name todos_overview --path features/todos/todos_implementation
✓ todos_overview_bloc.dart 已创建
✓ todos_overview_event.dart 已创建
✓ todos_overview_state.dart 已创建
核心特性
| 特性 | 说明 |
|---|---|
| 声明式 | 通过单一的 project.dart 文件声明整个项目结构 |
| 单一来源 | 所有依赖版本通过 package.dart 集中管理 |
| 规则即代码 | 架构违规会立即终止生成过程 |
安装
csharp
dart pub global activate flutist
前置条件 :Flutter SDK,并确保 ~/.pub-cache/bin 已添加到 PATH
快速开始
1. 初始化项目
bash
cd my_flutter_project
flutist init
Flutist 会根据上下文自动适配:
- • 无
pubspec.yaml:询问是否创建新的 Flutter 项目 - • 存在
pubspec.yaml:询问是新建项目还是迁移现有项目 -
- • 新项目:创建
app模块,添加到工作区,生成lib/main.dart - • 现有项目:仅创建配置文件,保留现有代码结构
- • 新项目:创建
2. 创建模块
css
# 创建 Clean 架构模块
flutist create --name login --path features --options clean
# 创建 Microfeature 架构模块
flutist create --name network --path packages --options micro
# 创建 Lite 模块
flutist create --name auth --path packages --options lite
# 创建单一包
flutist create --name utils --path core
3. 管理依赖
csharp
# 添加包(自动解析版本)
flutist pub add http bloc flutter_bloc
# 同步依赖到所有模块
flutist generate
4. 从自定义模板生成代码
bash
# 列出可用模板
flutist scaffold list
# 从模板生成
flutist scaffold feature --name login
flutist scaffold feature --name login --path lib/features
命令一览
| 命令 | 描述 | 用法 |
|---|---|---|
| init | 初始化新项目或现有项目 | flutist init |
| create | 创建新模块 | flutist create --name <name> --path <path> [--options <type>] |
| generate | 同步依赖并重新生成文件 | flutist generate |
| check | 检查架构规则(CI 友好,不修改文件) | flutist check |
| test | 并行运行所有模块的测试 | flutist test [-m <module>] |
| scaffold | 从模板生成代码 | flutist scaffold <template> --name <name> |
| pub | 管理依赖 | flutist pub add <package> |
| graph | 可视化模块依赖关系 | flutist graph [--format <format>] |
| help | 显示帮助信息 | flutist help [command] |
核心文件
| 文件 | 说明 |
|---|---|
package.dart |
外部包版本和模块名称的单一真实来源,多行格式为解析必需 |
project.dart |
声明模块依赖和模块间关系,由 flutist generate 读取 |
flutist_gen.dart |
自动生成的类型安全访问器,提供 IDE 自动补全支持 |
项目结构
典型的 Flutist 项目结构:
bash
my_project/
├── project.dart # 项目配置
├── package.dart # 集中式依赖管理
├── pubspec.yaml # 工作区配置
├── lib/ # 根应用代码
│ └── main.dart
├── app/ # 主应用模块
│ ├── lib/
│ │ └── app.dart
│ └── pubspec.yaml
├── features/ # 功能模块
│ └── auth/
│ ├── auth_domain/
│ ├── auth_data/
│ └── auth_presentation/
├── packages/ # 库模块
│ └── network/
│ ├── network_interface/
│ ├── network_implementation/
│ ├── network_testing/
│ ├── network_tests/
│ └── network_example/
└── flutist/
├── templates/ # 脚手架模板
└── flutist_gen.dart # 生成的代码
模块类型
flutist create 会生成层级包并自动在 project.dart 中配置依赖关系。
Clean 架构 (--options clean)
3 层 Clean Architecture,最适合需要清晰关注点分离的功能模块。
bash
features/login/
├── login_domain/ # 业务规则、实体、用例(无外部依赖)
├── login_data/ # 仓库、数据源、DTO
└── login_presentation/ # UI 和状态管理
自动配置依赖 :presentation → domain,data → domain
规则 :所有依赖箭头指向 domain,domain 不依赖任何东西。
Microfeature 架构 (--options micro)
5 层 Microfeature Architecture,最适合跨功能共享的可复用库。
bash
packages/network/
├── network_interface/ # 公共 API(抽象类、模型)
├── network_implementation/ # 具体实现
├── network_testing/ # 测试辅助、模拟对象
├── network_tests/ # 单元测试和集成测试
└── network_example/ # 模块演示应用
自动配置依赖 :implementation/testing → interface,tests/example → implementation + testing
规则 :消费者只依赖 interface,组合根注入实现。
Lite 架构 (--options lite)
4 层 Microfeature lite(无 example),最适合内部 API。
bash
packages/auth/
├── auth_interface/
├── auth_implementation/
├── auth_testing/
└── auth_tests/
单一包(省略 --options)
无层级,最适合工具类、共享模型或应用外壳。
bash
core/utils/
├── lib/
│ └── utils.dart
└── pubspec.yaml
架构验证
flutist generate 和 flutist check 自动执行以下规则:
| 规则 | 说明 |
|---|---|
| 实现引用 | 只有组合根(默认:app)和同功能测试/example 可以引用 _implementation 包 |
| 测试层隔离 | _testing 包被排除在生产依赖之外 |
| Example 独立性 | _example 模块不能被任何生产代码引用 |
| 方向强制 | 同功能层级遵循声明的依赖方向 |
| 循环依赖 | 通过 DFS 遍历检测,绝不允许 |
配置选项
php
// project.dart
ProjectOptions(
strictMode: true, // true(默认):违规时终止 / false:仅警告
compositionRoots: ['app'], // 允许引用 _implementation 的模块
)
Scaffold 脚手架模板
将重复性工作保存为模板,通过 flutist scaffold 自动化生成代码。
模板变量
在 .template 文件和 path 值中使用 {{变量}} 进行替换:
| 变量 | 输入 login_feature | 输出 |
|----------|--------------------|---------------|-----------------|
| `{{name | snake_case}}` | login_feature | login_feature |
| `{{name | pascal_case}}` | login_feature | LoginFeature |
| `{{name | camel_case}}` | login_feature | loginFeature |
| `{{name | upper_case}}` | login_feature | LOGIN_FEATURE |
template.yaml 结构
lua
description: "BLoC Feature Template"
attributes:
- name: name
required: true
- name: path
required: false
default: "lib/features"
items:
- type: file
path: "{{path}}/{{name | snake_case}}/{{name | snake_case}}_bloc.dart"
templatePath: "bloc.dart.template"
- type: string
path: "{{path}}/{{name | snake_case}}/README.md"
contents: |
# {{name | pascal_case}}
Item 类型
| 类型 | 说明 |
|---|---|
| file | 读取 .template 文件,替换变量后生成 |
| string | 使用内联内容直接生成文件 |
| directory | 复制整个模板目录 |
实战示例:BLoC Feature 模板
bloc.dart.template
javascript
import 'package:bloc/bloc.dart';
part '{{name | snake_case}}_event.dart';
part '{{name | snake_case}}_state.dart';
class {{name | pascal_case}}Bloc
extends Bloc<{{name | pascal_case}}Event, {{name | pascal_case}}State> {
{{name | pascal_case}}Bloc() : super(const {{name | pascal_case}}Initial()) {
on<{{name | pascal_case}}Started>(_onStarted);
}
Future<void> _onStarted(
{{name | pascal_case}}Started event,
Emitter<{{name | pascal_case}}State> emit,
) async {}
}
event.dart.template
vbnet
part of '{{name | snake_case}}_bloc.dart';
sealed class {{name | pascal_case}}Event {
const {{name | pascal_case}}Event();
}
final class {{name | pascal_case}}Started extends {{name | pascal_case}}Event {
const {{name | pascal_case}}Started();
}
state.dart.template
dart
part of '{{name | snake_case}}_bloc.dart';
sealed class {{name | pascal_case}}State {
const {{name | pascal_case}}State();
}
final class {{name | pascal_case}}Initial extends {{name | pascal_case}}State {
const {{name | pascal_case}}Initial();
}
final class {{name | pascal_case}}Loading extends {{name | pascal_case}}State {
const {{name | pascal_case}}Loading();
}
final class {{name | pascal_case}}Loaded<T> extends {{name | pascal_case}}State {
final T data;
const {{name | pascal_case}}Loaded(this.data);
}
final class {{name | pascal_case}}Error extends {{name | pascal_case}}State {
final String message;
const {{name | pascal_case}}Error(this.message);
}
运行生成
bash
$ flutist scaffold bloc_feature --name login --path lib/features
✓ lib/features/login/login_bloc.dart
✓ lib/features/login/login_event.dart
✓ lib/features/login/login_state.dart
实战示例:Riverpod Notifier 模板
notifier.dart.template
dart
import 'package:riverpod_annotation/riverpod_annotation.dart';
import '{{name | snake_case}}_state.dart';
part '{{name | snake_case}}_notifier.g.dart';
@riverpod
class {{name | pascal_case}}Notifier extends _${{name | pascal_case}}Notifier {
@override
{{name | pascal_case}}State build() => const {{name | pascal_case}}State.initial();
Future<void> load() async {
state = const {{name | pascal_case}}State.loading();
try {
state = const {{name | pascal_case}}State.loaded(null);
} catch (e) {
state = {{name | pascal_case}}State.error(e.toString());
}
}
}
state.dart.template
dart
import 'package:freezed_annotation/freezed_annotation.dart';
part '{{name | snake_case}}_state.freezed.dart';
@freezed
class {{name | pascal_case}}State with _${{name | pascal_case}}State {
const factory {{name | pascal_case}}State.initial() = _Initial;
const factory {{name | pascal_case}}State.loading() = _Loading;
const factory {{name | pascal_case}}State.loaded(dynamic data) = _Loaded;
const factory {{name | pascal_case}}State.error(String msg) = _Error;
}
示例项目
Clean Architecture 示例
flutist_clean_architecture[2]
- • Domain、Data、Presentation 三层 Clean Architecture
- • 集中式依赖管理
- • 大型 Flutter 应用最佳实践
Microfeature Architecture 示例
flutist_microfeature_architecture[3]
- • Interface、Implementation、Tests、Testing 四层 Microfeature 架构
- • 完全隔离的可复用库模块
- • 集中式依赖管理
相关链接
| 资源 | 链接 |
|---|---|
| 📦 pub.dev | pub.dev/packages/fl... |
| 📖 文档网站 | deepwiki.com/seonwooke/f... |
| 💻 GitHub | github.com/seonwooke/f... |
引用链接
[1] Tuist: tuist.io/
[2] flutist_clean_architecture: github.com/seonwooke/f...
[3] flutist_microfeature_architecture: github.com/seonwooke/f...