803 lines
11 KiB
Markdown
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。先保持简单,把页面跑起来、流程走通,再根据复杂度拆分。
|