Flutter 完全组件化的项目结构设计实践

Flutter 完全组件化的项目结构设计实践

在做 Flutter 项目的时候,随着业务不断扩展,如果所有代码都堆在 lib/ 目录里,后期维护会越来越痛苦。组件化(Componentization) 是一种常见的解决方案,它能让项目更清晰、更易扩展,团队协作也会更高效。本文结合实践,分享一种 Flutter 完全组件化的项目结构设计方案


为什么要组件化?

  • 高内聚,低耦合:每个模块(feature)独立,边界清晰。
  • 多人协作:不同的功能模块可以交给不同的开发同学,互不干扰。
  • 可复用性:部分模块可以直接复用到其他项目中。
  • 可维护性:改动时只需要关注单个模块,降低风险。

顶层目录结构

假设我们的项目叫 my_app,整体结构可以这样设计:

my_app/

├── apps/ # 主应用(App 容器)

│ └── main_app/ # 真正运行的壳工程

├── core/ # 核心基础层(工具 & SDK)

│ ├── network/ # 网络封装(dio/http)

│ ├── database/ # 本地存储(sqflite/hive)

│ ├── common_ui/ # 公共UI组件(按钮、弹窗、空页面)

│ ├── utils/ # 工具类(日志、加密、日期)

│ └── theme/ # 全局主题(颜色、文字样式)

├── features/ # 各业务功能模块

│ ├── home/ # 首页

│ ├── video/ # 视频模块(短视频/播放器)

│ ├── chat/ # 聊天模块

│ └── profile/ # 用户中心

├── shared/ # 跨模块共享的模型、服务

│ ├── models/ # 数据实体

│ └── services/ # 公共 Service(用户、配置、埋点)

├── plugins/ # 自研 Flutter 插件(原生能力)

│ ├── photo_picker/ # 相册选择插件

│ └── short_video_player # 短视频播放器插件

├── pubspec.yaml # 顶层依赖管理

└── README.md


Feature 模块内部结构

features/video/ 模块为例:

video/

├── lib/

│ ├── src/

│ │ ├── pages/ # 页面

│ │ ├── widgets/ # 模块内私有 widget

│ │ ├── controllers/ # 状态管理(GetX/Bloc)

│ │ ├── services/ # 数据仓库(repository)

│ │ └── models/ # 模块内的数据模型

│ └── video.dart # 对外暴露的统一入口(类似 index.dart)

├── pubspec.yaml # 模块独立依赖

设计要点:

  • 模块内自成体系,拥有页面、状态、数据。
  • 只暴露 video.dart 给外部使用,内部实现不对外开放。
  • 可以被其他项目直接引入复用。

模块间通信方式

1.路由解耦(基于 GetX)

使用 GetX 的命名路由来统一管理,避免模块间直接依赖页面类。

apps/main_app 中维护一个全局路由表,例如:

// app_routes.dart

dart 复制代码
   class AppRoutes {
     static const home = '/home';
     static const videoDetail = '/videoDetail';
     static const chat = '/chat';
     static const profile = '/profile';
   }

路由配置集中在 GetMaterialApp 中:

// main.dart

dart 复制代码
import 'package:get/get.dart';
import 'app_routes.dart';

void main() {
  runApp(
    GetMaterialApp(
      initialRoute: AppRoutes.home,
      getPages: [
        GetPage(name: AppRoutes.home, page: () => const HomePage()),
        GetPage(name: AppRoutes.videoDetail, page: () => const VideoDetailPage()),
        GetPage(name: AppRoutes.chat, page: () => const ChatPage()),
        GetPage(name: AppRoutes.profile, page: () => const ProfilePage()),
      ],
    ),
  );
}

业务模块跳转时,只需要依赖路由常量,而不是直接依赖页面类:

dart 复制代码
Get.toNamed('${AppRoutes.videoDetail}?id=$videoId');

这样一来,features/video 模块的内部页面不会被外部直接 import,达到解耦目的。

  1. 数据传递

使用 Get.arguments 或事件总线(EventBus/Stream/RxBus)来传递参数,而不是直接依赖模块。

// 跳转时传参

dart 复制代码
Get.toNamed(AppRoutes.videoDetail, arguments: {'id': videoId});

// 接收参数

dart 复制代码
final args = Get.arguments as Map;
final videoId = args['id'];

3.共享数据

放在 shared/ 或 core/services/ 中,使用 Get.find() 获取,避免 feature 之间直接耦合。


依赖管理策略

• 每个 feature 拥有独立的 pubspec.yaml,自行管理依赖。

• 模块之间禁止直接 import,只能依赖 core 和 shared。

• 顶层 pubspec.yaml 使用 dependency_overrides 来统一第三方依赖版本。


开发流程

  1. 新需求 → 新建 features/xxx 模块。

  2. 在 apps/main_app 中集成对应模块。

  3. 公共逻辑沉淀到 core 或 shared,避免重复。

  4. 原生能力统一封装到 plugins/,避免业务直接写 platform channel。


组件化带来的好处

• 团队协作更高效:模块独立,互不干扰。

• 扩展性强:新业务只需新增模块,不会污染现有代码。

• 维护成本低:定位 bug 或修改逻辑时,只需关注单一模块。

• 跨项目可复用:一些业务模块或插件可以直接抽出来独立使用。


模块依赖关系图

这张图的含义:

• main_app 是壳应用,依赖所有 features。

• 各个 features 只能依赖 core 和 shared,不能互相依赖。

• 插件 plugins 可以被某些 feature 调用,但也只依赖 core 公共能力。


总结

组件化不是 Flutter 独有的概念,但在中大型 Flutter 项目中,它能带来巨大的维护优势。本文给出的结构是一种通用的实践方案,你可以根据团队规模和业务复杂度,灵活调整。

相关推荐
不爱吃糖的程序媛9 分钟前
Flutter-OH 升级指导
flutter
恋猫de小郭2 小时前
Android 禁止侧载将正式实施,需要等待 24 小时冷静期
android·flutter·harmonyos
FFF-X3 小时前
解决 Flutter Gradle 下载报错:修改默认 distributionUrl
flutter
程序员Ctrl喵1 天前
异步编程:Event Loop 与 Isolate 的深层博弈
开发语言·flutter
前端不太难1 天前
Flutter 如何设计可长期维护的模块边界?
flutter
小蜜蜂嗡嗡1 天前
flutter列表中实现置顶动画
flutter
始持1 天前
第十二讲 风格与主题统一
前端·flutter
始持1 天前
第十一讲 界面导航与路由管理
flutter·vibecoding
始持1 天前
第十三讲 异步操作与异步构建
前端·flutter
新镜1 天前
【Flutter】 视频视频源横向、竖向问题
flutter