news 2026/11/6 2:03:44

Flutter Architecture Samples 全景指南:用同一 TodoMVC 应用对比 15+ 种 Flutter 架构方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Flutter Architecture Samples 全景指南:用同一 TodoMVC 应用对比 15+ 种 Flutter 架构方案
  • 示例工程

【免费下载链接】flutter_architecture_samples

TodoMVC for Flutter

项目地址:https://gitcode.com/gh_mirrors/fl/flutter_architecture_samples
点击查看免费下载

这篇技术指南以 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):

示例目录架构/核心工具一句话定位
vanillaFlutter 原生能力仅使用 Flutter 开箱即用的工具管理应用状态,也是规格说明指定的参考实现
inherited_widgetInheritedWidget用 InheritedWidget 沿 Widget 层级向下传递应用状态
change_notifier_providerChangeNotifier + provider使用 Flutter 的 ChangeNotifier 配合 Flutter 团队推荐的 provider 包
bloc_flutter手写 BLoC 模式以 Sinks 作为输入、Streams 作为输出的 BLoC 模式实现
bloc_librarybloc / flutter_bloc使用 bloc 与 flutter_bloc 库管理状态并驱动 Widget 更新
mobxMobX使用 Observable、Action、Reaction 三大概念管理状态并更新 Widget
reduxRedux使用 Redux 库管理应用状态并更新 Widget
simple_bloc_flutter"Simple" BLoC与 BLoC 类似,但输入用函数、输出用 Stream,代码量远少于标准 BLoC
mvi_flutterCycle.JS 思想将 Cycle.JS 的概念应用到 Flutter(MVI,Model-View-Intent)
states_rebuilderstates_rebuilder使用 states_rebuilder 库管理状态并更新 Widget
built_reduxbuilt_redux用 built_redux 库强制不可变性并管理应用状态
scoped_modelscoped_model用 scoped_model 库持有应用状态并通知 Widget 更新
firestore_reduxRedux + Cloud Firestore在 Redux 基础上增加 Cloud_Firestore 作为 Todos 数据库
mvudartea使用 dartea 库(Model-View-Update,即 Elm 风格架构)管理状态并更新 Widget
mvcmvc_pattern用 MVC 库实现传统 MVC 设计模式
frideos_libraryFrideos使用 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 run

Web

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 与应用规格深受其启发)。

快速上手路径建议

对于不同诉求的读者,仓库给出了清晰的学习路径:

  1. 快速体验:直接flutter run运行 vanilla 参考实现,再逐一对比 inherited_widget、bloc_flutter、redux 等示例的lib目录结构差异。
  2. 系统学习状态管理:从手写方案(vanilla、bloc_flutter、simple_bloc_flutter)入手,再过渡到库驱动方案(bloc_library、mobx、states_rebuilder、scoped_model、built_redux)。
  3. 研究数据层与云端集成:阅读 todos_repository_core 抽象 + todos_repository_local_storage、firebase_flutter_repository、firebase_rtdb_flutter_repository 三种实现,对照 firestore_redux 示例看 Redux 与云端数据库如何协作。
  4. 学习端到端测试:以 integration_tests/lib/integration_tests.dart 与 Page Object Model 为范本,理解「无论架构如何变化,行为契约不变」的测试思想,并在各示例的test_driver目录中查看驱动入口。

无论你是想为团队选型状态管理方案、梳理 Flutter 分层架构,还是寻找可复用的 repository 抽象与集成测试模式,这个仓库都以「同一应用、多套架构、统一规格、统一测试」的方式给出了可直接借鉴的参考。请记住 README 的提醒:这些实现并非权威范例,请结合自己的项目优先级灵活采纳。

  • 示例工程

【免费下载链接】flutter_architecture_samples

TodoMVC for Flutter

项目地址:https://gitcode.com/gh_mirrors/fl/flutter_architecture_samples
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/28 11:43:38

Lerobot+飞特舵机:低成本搭建智能机械臂实战全攻略

我最早接触Lerobot&#xff0c;是冲着Hugging Face团队在开源机器人领域搞的那套东西去的。当时第一反应是&#xff1a;一个搞大模型的开源社区&#xff0c;怎么跑来折腾机械臂了&#xff1f;结果细看之后发现&#xff0c;Lerobot这个项目比我想象中务实得多——它把数据采集、…

作者头像 李华
网站建设 2026/10/28 20:22:43

Linux环境下MySQL学习路径:从安装到锁与事务实战

很多刚开始接触Linux的朋友都会在同一个地方卡住&#xff1a;系统装好了&#xff0c;命令也敲得挺溜&#xff0c;但一说到“装个数据库用用”&#xff0c;立刻没了方向。MySQL作为互联网行业最普及的关系型数据库&#xff0c;基本是运维和开发绕不开的第一站。这篇博文就围绕在…

作者头像 李华
网站建设 2026/10/27 23:12:49

QEMU+RISC-V搭建虚拟AI芯片实验台:AGENT辅助软件栈验证实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/28 4:48:37

Altium Designer差分对设计实战:规则设置与高效走线全解析

1. 差分对设计前的思路梳理&#xff1a;为什么要花力气折腾规则做高速PCB设计的人&#xff0c;早晚都会碰到差分信号。USB、HDMI、PCIe、千兆网、LVDS&#xff0c;这些接口清一色都是差分对&#xff0c;跑起来动不动就是几百Mbps甚至几个Gbps。以前我刚接触Altium Designer&…

作者头像 李华
网站建设 2026/10/23 18:46:32

Agent架构设计中如何处理多轮交互的稳定性问题?

Agent架构设计中如何处理多轮交互的稳定性问题? 在AI Agent架构设计中,多轮交互的稳定性是确保系统可靠性和用户体验的关键。随着对话复杂度的增加,Agent需要在多个回合中保持上下文一致性、状态同步和错误恢复能力。本文将从技术原理、核心挑战、解决方案及实际案例等方面…

作者头像 李华
网站建设 2026/10/26 6:03:56

数据库事务与InnoDB锁机制:从原理到线上排查

数据库事务与InnoDB锁是怎么纠缠到一起的&#xff1f;我刚开始学MySQL的时候想得很简单&#xff0c;以为事务就是begin、commit、rollback三个关键字&#xff0c;锁就是“某行数据加锁了别人不能改”。真到了线上排查问题时才发现&#xff0c;这两样东西压根是同一枚硬币的两面…

作者头像 李华