Flutter MVVM 架构实践:结合 Bloc、模块化与路由解耦

✨ 写在前面

在移动应用开发中,架构设计决定了项目能否高效演进、团队协作是否顺畅、以及未来是否容易维护。在 Flutter 中,我们可以借助 Bloc 来实现一个专业、清晰、易测试的 MVVM 架构。本篇将从底层设计理念,到模块组织,再到状态管理实现,完整解析一个可开源、可商用的 Flutter 项目结构。


🧱 核心理念

本架构基于以下设计原则:

  • MVVM 模式解耦 UI 与逻辑

  • Bloc 做为 ViewModel 的核心实现

  • 模块化 + 路由解耦 支持独立业务开发和动态扩展

  • 支持开源、测试、生产等多种环境切换


📂 项目结构总览

lib/
├── core/                  # 基础设施层(技术支撑)
│   ├── exceptions/        # 全局异常定义
│   ├── network/           # 网络请求封装(如 Dio)
│   ├── utils/             # 通用工具类
│   └── router/            # 全局路由解耦与管理

├── common/                # 通用层(跨业务模块可复用)
│   ├── widgets/           # 通用 UI 组件
│   ├── models/            # 通用业务模型
│   └── services/          # 全局服务(如用户登录状态)

└── module/                # 各业务模块(横向拆分,独立开发)
    └── profile/           # 示例模块:用户资料
        ├── model/         # 业务模型,如 UserModel
        ├── view/          # 页面层(UI 展示)
        ├── bloc/          # 状态管理(ViewModel 逻辑)
        ├── repository/    # 数据仓库(含接口调用)
        └── router/        # 页面路径定义


🧠 为什么使用 Bloc 实现 ViewModel?

Bloc 是 Google 官方推荐的状态管理工具,具有以下优势:

  • 完美奏合 MVVM 中的 ViewModel 职责

  • 强类型、明确的事件(Event)与状态(State)设计

  • 易于测试和调试

  • 良好的社区生态,文档丰富

💡在本架构中,每个页面的 ViewModel 就是一个 Bloc 实例,通过监听事件更新状态,再由 UI 层消费这些状态。


🗉 路由设计:模块解耦的关键

使用 BaseModuleRoute 协议,统一管理每个模块的路由信息,实现模块注册与导出:

abstract class BaseModuleRoute {
  String get moduleName;
  String get entryPath;
  Map<String, WidgetBuilder> get routes;
  List<String> get exportedPages;
}

全局路由集中在 AppRouter,负责聚合、跳转、容错处理:

static final List<BaseModuleRoute> _modules = [
  HomeModuleRoute(),
  ProfileModuleRoute(),
];

支持路径 + 参数跳转、URI 解析、找不到页面兜底等功能。


🔄 MVVM 数据流:从 Event 到 UI

Bloc (ViewModel) 层

class UserProfileBloc extends Bloc<UserProfileEvent, UserProfileState> {
  final UserRepository repository;

  UserProfileBloc(this.repository) : super(UserProfileInitial()) {
    on<LoadUserProfile>((event, emit) async {
      emit(UserProfileLoading());
      try {
        final user = await repository.fetchUserProfile();
        emit(UserProfileLoaded(user));
      } catch (e) {
        emit(UserProfileError("加载失败"));
      }
    });
  }
}

View 层(Page)

BlocBuilder<UserProfileBloc, UserProfileState>(
  builder: (context, state) {
    if (state is UserProfileLoading) return CircularProgressIndicator();
    if (state is UserProfileLoaded) return Text(state.user.name);
    if (state is UserProfileError) return Text("错误:${state.message}");
    return SizedBox.shrink();
  },
)

用户触发动作 -> add(event) -> Bloc 处理 -> emit(state) -> UI 层响应状态变化。


💪 易测试、易拟合

由于:

  • Bloc 是纯 Dart 类,可独立测试(无需 Widget 测试)

  • 所有逻辑集中在 ViewModel 层(Bloc),职责清晰

  • 各模块独立、UI 纯粹,易于重构和复用

使得此架构非常适合团队协作、大型项目、组件化中台建设等复杂场景。


✅ 总结

架构点 是否具备 说明
MVVM 模式 ViewModel 即 Bloc,解耦逻辑与 UI
Bloc 状态管理 拆分 Event/State,强类型、易扩展
模块化开发 支持独立模块注册路由与入口
路由解耦与跳转 Uri/路径解析、参数传递、兜底页面
易于测试与扩展 纯 Dart Bloc + Repository 架构

📚 推荐工具 & 插件


📌 最后的话

架构并非“一刀切”方案,而是实践总结与演进结果。 适合自己的才是最好的。
github示例:
https://github.com/Greathao/bloc_mvvm_architecture
bloc:
https://bloclibrary.dev/getting-started/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • """1.个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello ...
    她即我命阅读 9,098评论 0 6
  • 为了让我有一个更快速、更精彩、更辉煌的成长,我将开始这段刻骨铭心的自我蜕变之旅!从今天开始,我将每天坚持阅...
    李薇帆阅读 6,380评论 1 4
  • 似乎最近一直都在路上,每次出来走的时候感受都会很不一样。 1、感恩一直遇到好心人,很幸运。在路上总是...
    时间里的花Lily阅读 5,437评论 1 3
  • 1、expected an indented block 冒号后面是要写上一定的内容的(新手容易遗忘这一点); 缩...
    庵下桃花仙阅读 3,848评论 0 2
  • 一、工具箱(多种工具共用一个快捷键的可同时按【Shift】加此快捷键选取)矩形、椭圆选框工具 【M】移动工具 【V...
    墨雅丫阅读 4,251评论 0 0

友情链接更多精彩内容