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

11 KiB

常见 App 项目结构

Flutter 项目里,真正主要写代码的是 lib/。一个常见、可维护的 App 结构通常不是按“页面类型”乱放,而是按“应用层级 + 功能模块”来组织。

推荐结构大概是这样:

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

应用入口。

通常只做很少的事情:

void main() {
  runApp(const App());
}

如果项目复杂一点,可能还会做:

  • 初始化 Flutter 绑定
  • 初始化本地存储
  • 初始化日志
  • 初始化 Firebase
  • 初始化依赖注入
  • 捕获全局异常

例如:

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(const App());
}

原则是:main.dart 不应该写页面 UI,也不应该堆业务逻辑。

app/

放整个 App 级别的配置。

常见内容:

app/
  app.dart
  router.dart
  theme.dart

app.dart

放根组件,一般是 MaterialAppMaterialApp.router

例如:

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,比如:

/login
/home
/profile
/settings

theme.dart

放主题配置,例如:

  • 主色
  • 字体
  • 按钮样式
  • AppBar 样式
  • 输入框样式
  • 明暗主题

core/

放“全局基础能力”。

这些东西通常不属于某个具体页面或业务模块,而是整个 App 都可能用。

常见结构:

core/
  config/
  constants/
  network/
  storage/
  errors/
  utils/

core/config/

放环境配置,例如:

dev
test
prod

比如:

class AppConfig {
  static const apiBaseUrl = 'https://api.example.com';
}

core/constants/

放全局常量,例如:

class AppConstants {
  static const appName = 'Patbond';
}

可以包括:

  • App 名称
  • 默认分页大小
  • 缓存 key
  • 接口超时时间
  • 正则表达式

core/network/

放网络请求相关代码,例如:

network/
  api_client.dart
  api_response.dart
  interceptors.dart

如果用 dio,通常会在这里封装:

  • baseUrl
  • 请求头
  • token
  • 错误处理
  • 超时
  • 日志拦截器

core/storage/

放本地存储封装,例如:

  • SharedPreferences
  • SecureStorage
  • Hive
  • SQLite

比如:

storage/
  local_storage.dart
  secure_storage.dart

core/errors/

放错误类型和异常处理。

例如:

errors/
  app_exception.dart
  failure.dart

core/utils/

放通用工具方法。

例如:

  • 日期格式化
  • 金额格式化
  • 防抖
  • 校验函数
  • 文件大小转换

注意:不要把业务逻辑都塞进 utils/,否则它会变成垃圾桶目录。

features/

这是业务模块目录,也是实际开发最常动的地方。

一个功能一个文件夹,比如:

features/
  auth/
  home/
  profile/
  order/
  settings/

例如登录注册相关都放在:

features/auth/

首页相关放在:

features/home/

个人资料放在:

features/profile/

Feature 内部结构

中大型项目里,一个 feature 常见会分成三层:

features/auth/
  data/
  domain/
  presentation/

这套结构接近 Clean Architecture,但不一定要一开始就完整照搬。

data/

负责数据来源。

常见内容:

data/
  models/
  repositories/
  services/

例如:

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/

负责业务规则。

常见内容:

domain/
  entities/
  repositories/
  usecases/

例如:

features/auth/domain/
  entities/
    user.dart
  repositories/
    auth_repository.dart
  usecases/
    login.dart
    logout.dart

domain 理论上不关心数据来自哪里。

它只关心业务是什么。

例如:

abstract class AuthRepository {
  Future<User> login(String email, String password);
}

然后 data 层去实现它。

小项目可以先不拆 domain/,直接用 data + presentation 就够了。

presentation/

负责 UI 和页面状态。

常见内容:

presentation/
  pages/
  widgets/
  controllers/

例如:

features/auth/presentation/
  pages/
    login_page.dart
    register_page.dart
  widgets/
    login_form.dart
  controllers/
    login_controller.dart

这里一般放:

  • 页面
  • 组件
  • ViewModel / Controller / Bloc / Provider
  • 页面状态类

如果用 Riverpod,可能是:

login_controller.dart
auth_provider.dart

如果用 Bloc,可能是:

auth_bloc.dart
auth_event.dart
auth_state.dart

shared/

放跨模块复用的 UI 或类型。

常见结构:

shared/
  widgets/
  models/
  extensions/

shared/widgets/

通用组件,例如:

primary_button.dart
empty_view.dart
loading_view.dart
app_text_field.dart
network_image_view.dart

这些组件不属于某个具体业务模块,多个页面都能用。

shared/models/

通用模型,例如:

pagination.dart
option_item.dart
result.dart

shared/extensions/

Dart 扩展方法,例如:

extension StringX on String {
  bool get isBlank => trim().isEmpty;
}

generated/

放自动生成代码。

例如:

  • 国际化生成文件
  • 路由生成文件
  • JSON 序列化生成文件
  • Assets 生成文件

这个目录通常不要手写业务逻辑。

资源目录

Flutter 资源通常不放在 lib/ 里,而是放在项目根目录。

常见结构:

assets/
  images/
  icons/
  fonts/
  lottie/

然后在 pubspec.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

测试目录

常见测试结构:

test/
  features/
    auth/
      login_controller_test.dart
  core/
    network/
      api_client_test.dart

测试可以按 lib/ 的结构对应放。

例如:

lib/features/auth/presentation/controllers/login_controller.dart

对应测试:

test/features/auth/presentation/controllers/login_controller_test.dart

小项目推荐结构

如果项目刚开始,不建议一上来就搞得太复杂。

当前项目可以先用这个结构:

lib/
  main.dart

  app/
    app.dart
    theme.dart

  features/
    home/
      home_page.dart

  shared/
    widgets/

这就够了。

等有登录、接口、缓存、状态管理之后,再扩展成:

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/

不建议一开始这样做

不建议刚开始就建一堆空目录:

core/
  errors/
  failures/
  usecases/
  validators/
  services/
  managers/
  helpers/
  providers/
  mixins/

这样看起来很专业,但实际开发时容易变成:

  • 不知道代码该放哪
  • 文件很多但内容很少
  • 过度抽象
  • 后期维护成本高

最好的做法是:随着需求出现再拆。

常见页面模块示例

假设要做一个带登录、首页、个人中心的 App,可以这样组织:

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.dartapp/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/

放代码时的判断标准

页面放哪里:

features/模块名/presentation/

例如登录页:

features/auth/presentation/login_page.dart

接口请求放哪里:

features/模块名/data/

例如登录接口:

features/auth/data/auth_api.dart

多个模块共用的网络封装放哪里:

core/network/

例如:

core/network/api_client.dart

多个页面共用的按钮放哪里:

shared/widgets/

例如:

shared/widgets/primary_button.dart

App 主题放哪里:

app/theme.dart

路由放哪里:

app/router.dart

常量放哪里:

core/constants/

图片放哪里:

assets/images/

对当前项目的建议

现在最适合从这个结构开始:

lib/
  main.dart
  app/
    app.dart
    theme.dart
  features/
    home/
      home_page.dart
  shared/
    widgets/

等开始做真实功能,比如登录、个人中心、业务列表,再扩展成:

features/
  auth/
  profile/
  bond/
  settings/

如果只是练手或做 MVP,不要过早引入完整 Clean Architecture。先保持简单,把页面跑起来、流程走通,再根据复杂度拆分。