- 示例工程
【免费下载链接】flutter_architecture_samples
TodoMVC for Flutter
这篇技术指南以 flutter_architecture_samples 开源仓库的 README 为核心主线,系统梳理项目为解决 Flutter 应用架构混乱问题而设计的多种架构示例、支撑代码包、运行方式与统一应用规范。读完本文,你将掌握该仓库中每个示例(Vanilla、InheritedWidget、BLoC、Redux、MobX、MVI、MVU、MVC 等)的定位与源码组织方式,理解其配套的 repository 抽象层与数据持久化方案,并学会在 iOS、Android 与 Web 三个平台上直接运行和测试这些示例。
项目背景:为什么需要一套 Flutter 架构对比样本
Flutter 在如何组织和架构应用方面提供了极大的灵活性。这种自由非常有价值,但也容易导致应用出现大而臃肿的类、不一致的命名方案,以及架构的缺失或风格混杂——这些问题都会让应用的测试、维护和扩展变得困难。
Flutter Architecture Samples 项目正是为解决或规避这些常见问题而生的:它用同一个应用(一个 TodoMVC 风格的任务管理应用)在不同架构理念与工具下实现,让开发者可以直观对比各方案在代码结构、架构设计和可测试性上的差异。你可以把这些示例当作学习参考,也可以作为自己创建应用的起点。项目的核心关注点是展示如何组织代码、设计架构,以及采用这些模式对测试和维护应用带来的最终影响。
需要强调的是,README 明确指出:这些示例不是权威范例(canonical examples)。你在自己的项目里可以用多种不同的方式应用这些技术,具体的取舍取决于你自己的优先级,因此不应将这些样本视为唯一正确的标准实现。为了保证焦点集中在架构本身,示例应用刻意使用了简单的 UI(应用规格说明 对此有详细定义)。
示例总览:16 个架构样本一览
仓库将同一 TodoMVC 应用分别用以下架构理念实现,每个示例均位于根目录下对应的子目录中(来源:README.md):
| 示例目录 | 架构/核心工具 | 一句话定位 |
|---|---|---|
| vanilla | Flutter 原生能力 | 仅使用 Flutter 开箱即用的工具管理应用状态,也是规格说明指定的参考实现 |
| inherited_widget | InheritedWidget | 用 InheritedWidget 沿 Widget 层级向下传递应用状态 |
| change_notifier_provider | ChangeNotifier + provider | 使用 Flutter 的 ChangeNotifier 配合 Flutter 团队推荐的 provider 包 |
| bloc_flutter | 手写 BLoC 模式 | 以 Sinks 作为输入、Streams 作为输出的 BLoC 模式实现 |
| bloc_library | bloc / flutter_bloc | 使用 bloc 与 flutter_bloc 库管理状态并驱动 Widget 更新 |
| mobx | MobX | 使用 Observable、Action、Reaction 三大概念管理状态并更新 Widget |
| redux | Redux | 使用 Redux 库管理应用状态并更新 Widget |
| simple_bloc_flutter | "Simple" BLoC | 与 BLoC 类似,但输入用函数、输出用 Stream,代码量远少于标准 BLoC |
| mvi_flutter | Cycle.JS 思想 | 将 Cycle.JS 的概念应用到 Flutter(MVI,Model-View-Intent) |
| states_rebuilder | states_rebuilder | 使用 states_rebuilder 库管理状态并更新 Widget |
| built_redux | built_redux | 用 built_redux 库强制不可变性并管理应用状态 |
| scoped_model | scoped_model | 用 scoped_model 库持有应用状态并通知 Widget 更新 |
| firestore_redux | Redux + Cloud Firestore | 在 Redux 基础上增加 Cloud_Firestore 作为 Todos 数据库 |
| mvu | dartea | 使用 dartea 库(Model-View-Update,即 Elm 风格架构)管理状态并更新 Widget |
| mvc | mvc_pattern | 用 MVC 库实现传统 MVC 设计模式 |
| frideos_library | Frideos | 使用 Frideos 库通过流管理应用状态并更新 Widget |
从这份清单可以看出仓库的覆盖思路:既有纯手写、零第三方依赖的「原生派」方案(vanilla、inherited_widget、bloc_flutter、simple_bloc_flutter),也有围绕成熟状态管理库的「库驱动」方案(bloc、MobX、Redux、MobX、scoped_model、states_rebuilder、built_redux、dartea、mvc_pattern、Frideos),还有面向数据层与后端的「云端」方案(firestore_redux)。同一 UI、同一交互、同一测试集,架构却各不相同——这正是该项目最核心的学习价值。
支撑代码:可复用的核心抽象层
除了 16 个示例应用,仓库还提供了一批被多个示例共享的支撑包(来源:README.md 的 Supporting Code 一节)。
集成测试套件
integration_tests 演示了如何用Page Object Model编写 Selenium 风格的集成(端到端)测试。这套测试套件会针对所有示例运行,这意味着无论底层架构如何变化,应用的外部行为都必须保持一致——这是验证各架构实现「行为等价」的关键机制。测试入口位于 integration_tests/lib/integration_tests.dart,其中覆盖了「加载时显示 loading 屏」「点击条目查看详情」「在详情页删除 todo 并弹出 SnackBar」「按完成/未完成过滤」「查看统计」「切换完成状态」「清除已完成」「全部完成切换」「新增 todo」「编辑 todo」等完整流程;页面对象封装在 integration_tests/lib/page_objects(共 13 个文件)。测试依赖 flutter_driver 与 test 包(见 integration_tests/pubspec.yaml),并通过各示例的 test_driver 目录下的入口运行。
Todos 数据层抽象
todos_repository_core 定义了加载与保存数据的核心抽象类,使得存储可以多种方式实现——例如移动端的文件存储或 Firebase,Web 端的window.localStorage。核心抽象定义在 todos_repository_core/lib/src/todos_repository.dart:
abstract class TodosRepository { /// Loads todos first from File storage. If they don't exist or encounter an /// error, it attempts to load the Todos from a Web Client. Future<List<TodoEntity>> loadTodos(); // Persists todos to local disk and the web Future saveTodos(List<TodoEntity> todos); }其设计意图在源码注释中写得非常清楚:领域层只依赖这个抽象类,具体应用则根据运行环境(Web 或 Flutter)注入正确的实现。数据实体 TodoEntity 包含complete、id、note、task四个字段,并实现了toJson/fromJson序列化,方便在不同存储介质间流转。
三种具体存储实现
- todos_repository_local_storage:以文件系统、
window.localStorage和 SharedPreferences 作为数据源实现 todos repository,对应的测试位于其 test 目录(含 file_storage、key_value_storage、reactive_repository、repository 四组测试)。 - firebase_flutter_repository:以Firestore作为数据源实现 todos repository。
- firebase_rtdb_flutter_repository:以Firebase 实时数据库(Realtime Database)作为数据源实现 todos repository。
UI 核心包
todos_app_core 则承载了所有示例共享的 UI 基础设施,位于 todos_app_core/lib/src:
- routes.dart 定义命名路由常量:
ArchSampleRoutes.home = '/'与ArchSampleRoutes.addTodo = '/addTodo',与 app_spec.md 中「必须有两个命名路由」的规格一一对应; - theme.dart 提供统一的
ArchSampleTheme主题; - keys.dart 提供
ArchSampleKeys(供测试定位 Widget 使用); - localization.dart 与 localizations 提供国际化支持(含英文文案 messages_en.dart);
- uuid.dart 提供 UUID 生成能力。
运行示例:iOS / Android 与 Web 的完整步骤
iOS / Android
在任何示例目录下执行(来源:README.md):
cd <sample_directory> flutter run例如运行参考实现:
cd vanilla flutter runWeb
Web 运行有明确的前提条件:需要 Flutter 版本不低于"Flutter 1.12.13+hotfix.6 • channel beta"。并且并非所有示例都支持 Web——运行前请检查示例目录中是否存在lib/main_web.dart文件(来源:README.md)。
cd <sample_directory> flutter run -d chrome -t lib/main_web.dart之所以要求lib/main_web.dart,是因为各示例为 Web 与移动端提供了不同的依赖注入入口:以 vanilla 为例,移动端入口 vanilla/lib/main.dart 使用 SharedPreferences 构建LocalStorageRepository,而 vanilla/lib/main_web.dart 则会改用window.localStorage类存储。这种「入口按平台分文件、核心逻辑共享」的做法,正是TodosRepository抽象层设计价值的直接体现。
依赖与包管理
示例通过 path 依赖引用共享包。例如 vanilla/pubspec.yaml 中:
dependencies: flutter: sdk: flutter todos_app_core: path: ../todos_app_core todos_repository_local_storage: path: ../todos_repository_local_storage key_value_store_flutter: key_value_store_web: shared_preferences: dev_dependencies: flutter_test: sdk: flutter flutter_driver: sdk: flutter test: mockito: integration_tests: path: ../integration_tests可见示例既不重复造轮子,也不各自为政:共享的 UI 基础设施(todos_app_core)、数据层(todos_repository_local_storage)与测试套件(integration_tests)都以本地路径方式复用。
统一应用规格:所有示例必须满足的行为契约
README 引用了 app_spec.md 作为应用规格说明,所有示例都必须遵循同一套行为规范。理解这份规格是看懂各架构示例「在对比什么」的前提。
参考实现与代码规范
- vanilla 实现被指定为参考应用,也是实现新示例的基础;在动手实现前,建议先交互体验其他应用以了解其构建方式与行为。
- 代码必须用
dartfmt格式化,并使用 vanilla 实现的.analysis_options.yaml且无分析错误。 - 应用须同时支持 Android 与 iOS,且必须包含测试。
- 视觉外观默认使用基础包提供的 Theme 与 Widgets(即 todos_app_core 中的
ArchSampleTheme),除非为了演示替代实践而有必要例外。
用户界面行为
- 主界面:包含两个 Tab——Todos 列表与 Stats 统计。
- Todos 列表:加载完成前显示 loading 屏;支持「+」新增、点击进入详情、勾选完成/未完成、滑动删除(删除时必须弹出包含任务标题与 Undo 按钮的 SnackBar,点击 Undo 后条目追加回列表末尾)、右上角过滤、溢出菜单中「全部完成/清除已完成」。
- 新增 Todo:任务 TextField 自动聚焦;必须先
.trim()再校验非空,否则显示错误;Note 字段用于备注;「+」保存并返回列表,返回键则不创建。 - 详情页:显示任务与完整备注;可勾选完成状态(返回后主列表同步更新);可进入编辑页;右上角删除按钮删除后返回列表并弹出带 Undo 的 SnackBar。
- 编辑页:任务 TextField 打开时不聚焦;同样需
.trim()且非空;「✓」保存并返回详情页,返回键放弃修改。 - 过滤:支持 All / Active / Completed 三种视图,当前选中项高亮。
- 溢出菜单:存在未完成项时显示「Mark all complete」,全部完成时显示「Mark all incomplete」;「清除已完成」在无已完成项时隐藏。
- 统计页:显示已完成与进行中的数量;该 Tab 隐藏过滤菜单;溢出菜单操作会同步更新统计。
数据源分层
规格定义了三个持久化层级:内存缓存(快)、磁盘(文件、SQLiteDb,慢)、网络(很慢,以 Mock 演示)。同步策略非常简单:每次 get 操作依次尝试内存缓存 → 磁盘 → 网络;每次写/删操作则同步更新缓存、本地与远端。值得注意的是,首次请求网络之后便不再访问网络,且这些示例完全可以用 Mock Web 服务演示,无需真的搭建远程服务。
路由规范
应用必须提供两个命名路由:/(主 Tab 页)与/addTodo(新增页);详情页与编辑页既可以用命名路由,也可以直接用 Navigator push 新路由。这在共享包 routes.dart 中有常量定义:
class ArchSampleRoutes { static final home = '/'; static final addTodo = '/addTodo'; }测试要求
- 单元 / Widget 测试:各示例必须包含测试,因为「架构是否易于测试」正是选择方案的权衡因素之一;不要求穷尽 Widget 测试,但若架构利于测试则应做演示。
- 集成测试:所有示例都必须通过 integration_tests 套件,可参照现有示例的
test_driver目录实现。
源码级解读:以 vanilla 参考实现理解「架构对比」的落点
要真正理解这份 README 的用意,最直接的方式是看参考实现 vanilla 的源码结构。它完整映射了 app_spec 的全部要求,并且只有 4 个核心文件:
- vanilla/lib/app.dart:
VanillaApp持有整个AppState,在initState中调用widget.repository.loadTodos()加载数据,并把updateTodo、addTodo、removeTodo、toggleAll、clearCompleted等回调通过构造函数逐层传给HomeScreen与AddEditScreen——这就是「把状态提升到根组件」的典型形态。它甚至重写了setState,在每次状态变更后自动repository.saveTodos(...)完成持久化。 - vanilla/lib/models.dart:定义
AppState(含filteredTodos过滤逻辑、numActive/numCompleted统计、toggleAll/clearCompleted)、Todo实体(与 TodoEntity 互转)、以及AppTab、ExtraAction、VisibilityFilter三个枚举。可见,即便不依赖任何状态管理库,这些通用概念(过滤、统计、实体转换)也同样存在——架构对比的正是「这些概念由谁、在哪里、如何管理」。 - vanilla/lib/main.dart:入口按平台注入存储实现(SharedPreferences +
KeyValueStorage('vanilla', ...))。 - vanilla/lib/screens 与 vanilla/lib/widgets:提供
home_screen、add_edit_screen、detail_screen三个页面,以及todo_item、todo_list、filter_button、extra_actions_button、stats_counter等可复用组件。
对照之下,redux、bloc_library、mobx 等示例则把同样的行为拆分到 action/reducer、bloc/event、store/observable 等各自架构的核心构件中。同一份 app_spec、同一套 integration_tests,让这些方案的「结构差异」与「测试成本差异」变得可度量、可比较。
社区规范与协作约定
README 明确将本仓库定位为「各种架构的讨论平台」:欢迎对架构进行激烈而健康的辩论,同时要求彼此友善。贡献者加入讨论、提 issue、新增示例都非常欢迎,详细指引见 CONTRIBUTING.md 与 code-of-conduct.md。仓库所有代码以MIT 许可发布。这些理念直接受两个知名项目启发:TodoMVC(用各种 JS 框架实现的 Todo 应用)与 Android Architecture Blueprints(Android 生态中的同概念项目,本仓库的 UI 与应用规格深受其启发)。
快速上手路径建议
对于不同诉求的读者,仓库给出了清晰的学习路径:
- 快速体验:直接
flutter run运行 vanilla 参考实现,再逐一对比 inherited_widget、bloc_flutter、redux 等示例的lib目录结构差异。 - 系统学习状态管理:从手写方案(vanilla、bloc_flutter、simple_bloc_flutter)入手,再过渡到库驱动方案(bloc_library、mobx、states_rebuilder、scoped_model、built_redux)。
- 研究数据层与云端集成:阅读 todos_repository_core 抽象 + todos_repository_local_storage、firebase_flutter_repository、firebase_rtdb_flutter_repository 三种实现,对照 firestore_redux 示例看 Redux 与云端数据库如何协作。
- 学习端到端测试:以 integration_tests/lib/integration_tests.dart 与 Page Object Model 为范本,理解「无论架构如何变化,行为契约不变」的测试思想,并在各示例的
test_driver目录中查看驱动入口。
无论你是想为团队选型状态管理方案、梳理 Flutter 分层架构,还是寻找可复用的 repository 抽象与集成测试模式,这个仓库都以「同一应用、多套架构、统一规格、统一测试」的方式给出了可直接借鉴的参考。请记住 README 的提醒:这些实现并非权威范例,请结合自己的项目优先级灵活采纳。
- 示例工程
【免费下载链接】flutter_architecture_samples
TodoMVC for Flutter
相关推荐
如何永久激活IDM:3种简单方案完整使用指南
如何永久激活IDM:3种简单方案完整使用指南 IDM Activation Script是一款专为Internet Download Manager设计的开源激
CLIFlutter状态管理终极指南:16种架构方案性能深度对比
Flutter状态管理终极指南:16种架构方案性能深度对比 Flutter状态管理是构建高性能应用的核心挑战,本文将系统对比16种主流架构方案的实现原理与性能表
示例工程如何使用 Flutter Clean Architecture 构建高可维护的 Flutter 应用
如何使用 Flutter Clean Architecture 构建高可维护的 Flutter 应用 Flutter Clean Architecture 是一
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考