项目架构
良好的项目架构是可维护、可扩展代码的基础。本章介绍 Flutter 项目中常用的目录组织方式和架构模式。
推荐目录结构
对于中小型项目,推荐以下目录组织方式:
lib/
├── main.dart # 应用入口,只做初始化和启动
├── app.dart # 根 Widget(MaterialApp/CupertinoApp)
├── config/ # 配置常量
│ ├── theme.dart # 主题配置
│ ├── routes.dart # 路由配置
│ └── constants.dart # 全局常量(API 地址、缓存 key 等)
├── pages/ # 页面(每个页面一个文件夹)
│ ├── home/
│ │ └── home_page.dart
│ ├── login/
│ │ └── login_page.dart
│ └── profile/
│ └── profile_page.dart
├── widgets/ # 可复用组件
│ ├── loading_indicator.dart
│ ├── empty_state.dart
│ └── custom_button.dart
├── models/ # 数据模型
│ ├── user.dart
│ └── product.dart
├── services/ # 服务层(网络请求、本地存储等)
│ ├── api_service.dart
│ └── storage_service.dart
└── utils/ # 工具函数
├── date_formatter.dart
└── validators.dart核心原则
- 按功能分层:页面、组件、模型、服务各归其位
- 页面独立:每个页面一个文件夹,页面专属组件放在同目录下
- 共享组件提取:被多个页面使用的组件放到
widgets/ - 入口简洁:
main.dart只负责启动,不超过 20 行
大型项目目录结构
项目变大后,推荐按 功能模块 组织:
lib/
├── main.dart
├── app.dart
├── core/ # 核心公共层
│ ├── theme/
│ ├── routes/
│ ├── network/ # dio 封装、拦截器
│ └── storage/ # 本地存储封装
├── features/ # 按功能模块组织
│ ├── auth/ # 登录/注册模块
│ │ ├── auth_page.dart
│ │ ├── auth_controller.dart # 状态逻辑
│ │ ├── widgets/ # 模块专属组件
│ │ └── models/
│ ├── home/
│ │ ├── home_page.dart
│ │ └── widgets/
│ └── profile/
│ ├── profile_page.dart
│ └── widgets/
└── shared/ # 跨模块共享
├── widgets/
├── models/
└── utils/何时切换到功能模块结构?
当项目超过 10 个页面 或有 3 人以上 协作时,建议切换到功能模块结构。小项目用分层结构即可,过度设计反而增加复杂度。
架构模式选择
最简方案:StatefulWidget
适合个人小项目、Demo、学习阶段:
dart
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _count = 0;
void _increment() => setState(() => _count++);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: Text('$_count')),
floatingActionButton: FloatingActionButton(
onPressed: _increment,
child: const Icon(Icons.add),
),
);
}
}优点: 零依赖,上手最快
缺点: 逻辑和 UI 耦合,难以测试和复用
推荐方案:Provider + 分层
适合大多数正式项目,是 Flutter 官方推荐的状态管理方案:
dart
// 1. 控制器(逻辑层)
class CounterController extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// 2. 顶层注入
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterController(),
child: const MyApp(),
),
);
}
// 3. 页面(展示层)
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
final count = context.watch<CounterController>().count;
return Scaffold(
body: Center(child: Text('$count')),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterController>().increment(),
child: const Icon(Icons.add),
),
);
}
}优点: 逻辑与 UI 分离,可测试,社区成熟
缺点: 需要理解 Provider 机制
架构模式对比
| 模式 | 适用场景 | 复杂度 | 可测试性 |
|---|---|---|---|
| StatefulWidget | Demo / 学习 | ★☆☆ | ★☆☆ |
| Provider + 分层 | 中小型项目 | ★★☆ | ★★★ |
| Riverpod + 功能模块 | 大型项目 | ★★★ | ★★★ |
建议
初学者先掌握 StatefulWidget,理解状态管理的基本概念后,再学习 Provider。不要一开始就追求复杂架构。
常见错误
1. 把所有代码写在 main.dart
dart
// ❌ 错误:所有代码堆在一个文件
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget { ... }
class HomePage extends StatefulWidget { ... }
class _HomePageState extends State<HomePage> { ... }
class SettingsPage extends StatelessWidget { ... }
class UserModel { ... }
class ApiService { ... }dart
// ✅ 正确:按职责拆分到不同文件
// main.dart - 只做启动
// pages/home/home_page.dart
// pages/settings/settings_page.dart
// models/user.dart
// services/api_service.dart2. 过度封装
dart
// ❌ 过度封装:只有一个地方用,没必要单独建文件
widgets/buttons/primary_action_button/
├── primary_action_button.dart
└── primary_action_button_style.dartdart
// ✅ 合理封装:至少 2-3 处复用时才提取
widgets/
└── custom_button.dart # 简洁明了3. 忽略分析规则
yaml
# analysis_options.yaml - 推荐配置
include: package:flutter_lints/flutter.yaml
linter:
rules:
prefer_const_constructors: true
prefer_const_declarations: true
avoid_print: true
prefer_single_quotes: true运行 flutter analyze 检查代码质量,在 CI/CD 中加入此步骤。
