架构案例研究
本指南中的代码示例来自指南针示例应用程序,该应用程序可帮助用户构建和预订行程。它是一个功能丰富、包含多个功能、路由和屏幕的健壮示例应用程序。该应用程序与 HTTP 服务器通信,具有开发和生产环境,包含品牌特有样式,并且测试覆盖率高。通过这些以及更多方式,它模拟了一个真实世界的、功能丰富的 Flutter 应用程序。




指南针应用程序的架构最接近 Flutter 应用架构指南 中描述的 MVVM 架构模式。本架构案例研究将通过介绍指南针应用程序的“主页”功能,演示如何实现这些指南。如果您不熟悉 MVVM,应首先阅读这些指南。
指南针应用程序的主屏幕显示用户账户信息以及用户保存的行程列表。从此屏幕您可以登出、打开详细行程页面、删除已保存的行程,以及导航到核心应用流程的第一页,该流程允许用户构建新行程。
在本案例研究中,您将学到以下内容:
- 如何在数据层中使用存储库和服务的 Flutter 应用架构指南,以及在UI 层中使用 MVVM 架构模式。
- 如何使用命令模式来安全地渲染随数据变化而变化的 UI。
- 如何使用
ChangeNotifier
和Listenable
对象来管理状态。 - 如何使用
package:provider
实现依赖注入。 - 在遵循推荐架构时,如何设置测试。
- 适用于大型 Flutter 应用的有效包结构。
本案例研究的编写顺序为:前面的页面可能会引用后面的页面。
本案例研究中的代码示例包含了理解架构所需的所有细节,但它们不是完整的、可运行的片段。如果您想跟随完整的应用程序,可以在 GitHub 上找到它。
包结构
#组织良好的代码便于多名工程师协作,最大限度地减少代码冲突,并且便于新工程师导航和理解。代码组织同时受益于并促进了定义明确的架构。
有两种流行的代码组织方式:
- 按功能组织 - 每个功能所需的类将一起分组。例如,您可以有一个
auth
目录,其中包含auth_viewmodel.dart
、login_usecase.dart
、logout_usecase.dart
、login_screen.dart
、logout_button.dart
等文件。 - 按类型组织 - 每种“类型”的架构都将一起分组。例如,您可以拥有
repositories
、models
、services
和viewmodels
等目录。
本指南推荐的架构倾向于结合这两种方式。数据层对象(存储库和服务)不与单个功能绑定,而 UI 层对象(视图和视图模型)则绑定。以下是 Compass 应用程序中代码的组织方式。
lib
├─┬─ ui
│ ├─┬─ core
│ │ ├─┬─ ui
│ │ │ └─── <shared widgets>
│ │ └─── themes
│ └─┬─ <FEATURE NAME>
│ ├─┬─ view_model
│ │ └─── <view_model class>.dart
│ └─┬─ widgets
│ ├── <feature name>_screen.dart
│ └── <other widgets>
├─┬─ domain
│ └─┬─ models
│ └─── <model name>.dart
├─┬─ data
│ ├─┬─ repositories
│ │ └─── <repository class>.dart
│ ├─┬─ services
│ │ └─── <service class>.dart
│ └─┬─ model
│ └─── <api model class>.dart
├─── config
├─── utils
├─── routing
├─── main_staging.dart
├─── main_development.dart
└─── main.dart
// The test folder contains unit and widget tests
test
├─── data
├─── domain
├─── ui
└─── utils
// The testing folder contains mocks other classes need to execute tests
testing
├─── fakes
└─── models
大部分应用程序代码位于 data
、domain
和 ui
文件夹中。data
文件夹按类型组织代码,因为存储库和服务可以在不同功能之间以及被多个视图模型使用。ui
文件夹按功能组织代码,因为每个功能只有一个视图和一个视图模型。
此文件夹结构的其它值得注意的特点:
UI
文件夹还包含一个名为“core”的子目录。Core 包含由多个视图共享的 widget 和主题逻辑,例如带有品牌样式的按钮。domain
文件夹包含应用程序数据类型,因为它们被数据层和 UI 层使用。- 该应用程序包含三个“main”文件,它们充当应用程序用于开发、暂存和生产的不同入口点。
- 在与
lib
同一级别存在两个与测试相关的目录:test/
包含测试代码,并且其结构与lib/
匹配。testing/
是一个子包,其中包含可用于其他包测试代码的 mock 和其他测试实用程序。testing/
文件夹可以被描述为您不发布的应用程序的一个版本。它是被测试的内容。
指南针应用程序中还有其他与架构无关的代码。要查看完整的包结构,请在 GitHub 上查看。
其他架构选项
#本案例研究中的示例演示了一个应用程序如何遵守我们推荐的架构规则,但也可以编写许多其他示例应用程序。此应用程序的 UI 严重依赖于视图模型和 ChangeNotifier
,但也可以轻松地使用 stream 或由 riverpod
、flutter_bloc
和 signals
包提供的其他库来编写。此应用程序层之间的通信通过方法调用处理所有内容,包括轮询新数据。它也可以选择使用 stream 从存储库向视图模型公开数据,并且仍然遵守本指南涵盖的规则。
即使您严格遵循本指南,并且选择不引入其他库,您仍然需要做出决定:您是否会有一个域层?如果是,您将如何管理数据访问?答案很大程度上取决于单个团队的需求,因此没有唯一的正确答案。无论您如何回答这些问题,本指南中的原则都将帮助您编写可扩展的 Flutter 应用程序。
如果您仔细观察,难道所有的架构不都是 MVVM 吗?
反馈
#随着本网站的这一部分不断发展,我们欢迎您的反馈!