Files
patbond-flutter/doc/common_app_structure.md
T
2026-07-15 12:01:04 +08:00

803 lines
11 KiB
Markdown

# 常见 App 项目结构
Flutter 项目里,真正主要写代码的是 `lib/`。一个常见、可维护的 App 结构通常不是按“页面类型”乱放,而是按“应用层级 + 功能模块”来组织。
推荐结构大概是这样:
```text
lib/
main.dart
app/
app.dart
router.dart
theme.dart
core/
config/
constants/
network/
storage/
errors/
utils/
features/
auth/
data/
domain/
presentation/
home/
data/
domain/
presentation/
profile/
data/
domain/
presentation/
shared/
widgets/
models/
extensions/
generated/
```
这是一种比较适合中大型 App 的结构。小项目可以简化。
## `main.dart`
应用入口。
通常只做很少的事情:
```dart
void main() {
runApp(const App());
}
```
如果项目复杂一点,可能还会做:
- 初始化 Flutter 绑定
- 初始化本地存储
- 初始化日志
- 初始化 Firebase
- 初始化依赖注入
- 捕获全局异常
例如:
```dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const App());
}
```
原则是:`main.dart` 不应该写页面 UI,也不应该堆业务逻辑。
## `app/`
放整个 App 级别的配置。
常见内容:
```text
app/
app.dart
router.dart
theme.dart
```
### `app.dart`
放根组件,一般是 `MaterialApp``MaterialApp.router`
例如:
```dart
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Patbond',
theme: AppTheme.light,
home: const HomePage(),
);
}
}
```
### `router.dart`
放路由配置。
如果页面少,可以先不用这个文件。
如果后面页面多,建议用 `go_router`,比如:
```text
/login
/home
/profile
/settings
```
### `theme.dart`
放主题配置,例如:
- 主色
- 字体
- 按钮样式
- AppBar 样式
- 输入框样式
- 明暗主题
## `core/`
放“全局基础能力”。
这些东西通常不属于某个具体页面或业务模块,而是整个 App 都可能用。
常见结构:
```text
core/
config/
constants/
network/
storage/
errors/
utils/
```
### `core/config/`
放环境配置,例如:
```text
dev
test
prod
```
比如:
```dart
class AppConfig {
static const apiBaseUrl = 'https://api.example.com';
}
```
### `core/constants/`
放全局常量,例如:
```dart
class AppConstants {
static const appName = 'Patbond';
}
```
可以包括:
- App 名称
- 默认分页大小
- 缓存 key
- 接口超时时间
- 正则表达式
### `core/network/`
放网络请求相关代码,例如:
```text
network/
api_client.dart
api_response.dart
interceptors.dart
```
如果用 `dio`,通常会在这里封装:
- baseUrl
- 请求头
- token
- 错误处理
- 超时
- 日志拦截器
### `core/storage/`
放本地存储封装,例如:
- SharedPreferences
- SecureStorage
- Hive
- SQLite
比如:
```text
storage/
local_storage.dart
secure_storage.dart
```
### `core/errors/`
放错误类型和异常处理。
例如:
```text
errors/
app_exception.dart
failure.dart
```
### `core/utils/`
放通用工具方法。
例如:
- 日期格式化
- 金额格式化
- 防抖
- 校验函数
- 文件大小转换
注意:不要把业务逻辑都塞进 `utils/`,否则它会变成垃圾桶目录。
## `features/`
这是业务模块目录,也是实际开发最常动的地方。
一个功能一个文件夹,比如:
```text
features/
auth/
home/
profile/
order/
settings/
```
例如登录注册相关都放在:
```text
features/auth/
```
首页相关放在:
```text
features/home/
```
个人资料放在:
```text
features/profile/
```
## Feature 内部结构
中大型项目里,一个 feature 常见会分成三层:
```text
features/auth/
data/
domain/
presentation/
```
这套结构接近 Clean Architecture,但不一定要一开始就完整照搬。
### `data/`
负责数据来源。
常见内容:
```text
data/
models/
repositories/
services/
```
例如:
```text
features/auth/data/
models/
user_model.dart
login_request.dart
services/
auth_api_service.dart
repositories/
auth_repository_impl.dart
```
这里一般处理:
- API 请求
- JSON 解析
- 本地缓存
- 数据模型转换
例如 `UserModel.fromJson()` 通常就在 `data/models/`
### `domain/`
负责业务规则。
常见内容:
```text
domain/
entities/
repositories/
usecases/
```
例如:
```text
features/auth/domain/
entities/
user.dart
repositories/
auth_repository.dart
usecases/
login.dart
logout.dart
```
`domain` 理论上不关心数据来自哪里。
它只关心业务是什么。
例如:
```dart
abstract class AuthRepository {
Future<User> login(String email, String password);
}
```
然后 `data` 层去实现它。
小项目可以先不拆 `domain/`,直接用 `data + presentation` 就够了。
### `presentation/`
负责 UI 和页面状态。
常见内容:
```text
presentation/
pages/
widgets/
controllers/
```
例如:
```text
features/auth/presentation/
pages/
login_page.dart
register_page.dart
widgets/
login_form.dart
controllers/
login_controller.dart
```
这里一般放:
- 页面
- 组件
- ViewModel / Controller / Bloc / Provider
- 页面状态类
如果用 Riverpod,可能是:
```text
login_controller.dart
auth_provider.dart
```
如果用 Bloc,可能是:
```text
auth_bloc.dart
auth_event.dart
auth_state.dart
```
## `shared/`
放跨模块复用的 UI 或类型。
常见结构:
```text
shared/
widgets/
models/
extensions/
```
### `shared/widgets/`
通用组件,例如:
```text
primary_button.dart
empty_view.dart
loading_view.dart
app_text_field.dart
network_image_view.dart
```
这些组件不属于某个具体业务模块,多个页面都能用。
### `shared/models/`
通用模型,例如:
```text
pagination.dart
option_item.dart
result.dart
```
### `shared/extensions/`
Dart 扩展方法,例如:
```dart
extension StringX on String {
bool get isBlank => trim().isEmpty;
}
```
## `generated/`
放自动生成代码。
例如:
- 国际化生成文件
- 路由生成文件
- JSON 序列化生成文件
- Assets 生成文件
这个目录通常不要手写业务逻辑。
## 资源目录
Flutter 资源通常不放在 `lib/` 里,而是放在项目根目录。
常见结构:
```text
assets/
images/
icons/
fonts/
lottie/
```
然后在 `pubspec.yaml` 注册:
```yaml
flutter:
assets:
- assets/images/
- assets/icons/
- assets/lottie/
fonts:
- family: Inter
fonts:
- asset: assets/fonts/Inter-Regular.ttf
- asset: assets/fonts/Inter-Bold.ttf
weight: 700
```
## 测试目录
常见测试结构:
```text
test/
features/
auth/
login_controller_test.dart
core/
network/
api_client_test.dart
```
测试可以按 `lib/` 的结构对应放。
例如:
```text
lib/features/auth/presentation/controllers/login_controller.dart
```
对应测试:
```text
test/features/auth/presentation/controllers/login_controller_test.dart
```
## 小项目推荐结构
如果项目刚开始,不建议一上来就搞得太复杂。
当前项目可以先用这个结构:
```text
lib/
main.dart
app/
app.dart
theme.dart
features/
home/
home_page.dart
shared/
widgets/
```
这就够了。
等有登录、接口、缓存、状态管理之后,再扩展成:
```text
lib/
main.dart
app/
app.dart
router.dart
theme.dart
core/
network/
storage/
constants/
utils/
features/
auth/
data/
presentation/
home/
presentation/
profile/
data/
presentation/
shared/
widgets/
extensions/
```
## 不建议一开始这样做
不建议刚开始就建一堆空目录:
```text
core/
errors/
failures/
usecases/
validators/
services/
managers/
helpers/
providers/
mixins/
```
这样看起来很专业,但实际开发时容易变成:
- 不知道代码该放哪
- 文件很多但内容很少
- 过度抽象
- 后期维护成本高
最好的做法是:随着需求出现再拆。
## 常见页面模块示例
假设要做一个带登录、首页、个人中心的 App,可以这样组织:
```text
lib/
main.dart
app/
app.dart
router.dart
theme.dart
core/
network/
api_client.dart
storage/
token_storage.dart
constants/
app_constants.dart
features/
auth/
data/
auth_api.dart
auth_repository.dart
presentation/
login_page.dart
register_page.dart
auth_controller.dart
home/
presentation/
home_page.dart
home_controller.dart
widgets/
home_card.dart
profile/
data/
profile_api.dart
profile_repository.dart
presentation/
profile_page.dart
edit_profile_page.dart
shared/
widgets/
primary_button.dart
app_text_field.dart
loading_view.dart
empty_view.dart
```
## 常见开发流程
一般做 App 时,顺序可以是:
1. 搭好 `main.dart``app/app.dart`
2. 建立主题 `app/theme.dart`
3. 创建首页 `features/home/home_page.dart`
4. 如果有多页面,再加 `app/router.dart`
5. 如果有接口,再加 `core/network/`
6. 如果有登录 token,再加 `core/storage/`
7. 每做一个功能,就在 `features/` 下新建一个模块
8. 多个模块共用的组件,再提到 `shared/widgets/`
## 放代码时的判断标准
页面放哪里:
```text
features/模块名/presentation/
```
例如登录页:
```text
features/auth/presentation/login_page.dart
```
接口请求放哪里:
```text
features/模块名/data/
```
例如登录接口:
```text
features/auth/data/auth_api.dart
```
多个模块共用的网络封装放哪里:
```text
core/network/
```
例如:
```text
core/network/api_client.dart
```
多个页面共用的按钮放哪里:
```text
shared/widgets/
```
例如:
```text
shared/widgets/primary_button.dart
```
App 主题放哪里:
```text
app/theme.dart
```
路由放哪里:
```text
app/router.dart
```
常量放哪里:
```text
core/constants/
```
图片放哪里:
```text
assets/images/
```
## 对当前项目的建议
现在最适合从这个结构开始:
```text
lib/
main.dart
app/
app.dart
theme.dart
features/
home/
home_page.dart
shared/
widgets/
```
等开始做真实功能,比如登录、个人中心、业务列表,再扩展成:
```text
features/
auth/
profile/
bond/
settings/
```
如果只是练手或做 MVP,不要过早引入完整 Clean Architecture。先保持简单,把页面跑起来、流程走通,再根据复杂度拆分。