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