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

A screenshot of the splash screen of the compass app.
A screenshot of the home screen of the compass app.
A screenshot of the search form screen of the compass app.
A screenshot of the booking screen of the compass app.

Compass 应用的架构最类似于 MVVM 架构模式,如 Flutter 的 应用架构指南 中所述。本架构案例研究通过 Compass 应用的“Home”功能,演示了如何实现这些指南。如果您不熟悉 MVVM,应首先阅读这些指南。

Compass 应用的“Home”主屏幕显示用户账户信息和用户已保存的行程列表。从该屏幕,您可以退出登录、打开详细行程页面、删除已保存行程,并导航到核心应用流程的第一页,该页面允许用户创建新的行程。

在本案例研究中,您将学到以下内容:

本案例研究旨在按顺序阅读。任何页面都可能引用前一页的内容。

本案例研究中的代码示例包含了理解架构所需的所有细节,但它们并非完整、可运行的代码片段。如果您想查看完整的应用,可以在 GitHub 上找到它。

包结构

#

组织良好的代码更容易让多名工程师协作,代码冲突更少,也更便于新工程师理解和上手。代码组织既受益于明确定义的架构,也反过来促进架构的良好定义。

有两种流行的代码组织方式:

  1. 按功能组织——每个功能所需的类都组合在一起。例如,您可能有一个 auth 目录,其中包含 auth_viewmodel.dartlogin_usecase.dartlogout_usecase.dartlogin_screen.dartlogout_button.dart 等文件。
  2. 按类型组织——每种架构“类型”都组合在一起。例如,您可能拥有 repositoriesmodelsservicesviewmodels 等目录。

本指南中推荐的架构适用于这两种方式的结合。数据层对象(仓库和服务)不绑定于单一功能,而 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

大部分应用代码位于 datadomainui 文件夹中。data 文件夹按类型组织代码,因为仓库和服务可以在不同功能和多个视图模型之间共享使用。ui 文件夹按功能组织代码,因为每个功能都只有一个视图和一个视图模型。

此文件夹结构的其他显著特点:

  • UI 文件夹还包含一个名为“core”的子目录。Core 包含多个视图共享的组件(widgets)和主题逻辑,例如带有品牌样式的按钮。
  • domain 文件夹包含应用的数据类型,因为它们被数据层和 UI 层使用。
  • 该应用包含三个“main”文件,它们作为应用在开发、预发布和生产环境中的不同入口点。
  • 在与 lib 同级的位置,有两个与测试相关的目录:test/ 存放测试代码,其结构与 lib/ 匹配。testing/ 是一个子包,包含模拟对象(mocks)和其他测试工具,可在其他包的测试代码中使用。testing/ 文件夹可以被描述为您的应用中不随产品发布的部分。它是被测试的内容。

Compass 应用中还有其他不属于架构相关的代码。要查看完整的包结构,请在 GitHub 上查看

其他架构选项

#

本案例研究中的示例演示了一个应用如何遵循我们推荐的架构规则,但还有许多其他示例应用也可以用不同的方式编写。此应用的 UI 大量依赖于视图模型和 ChangeNotifier,但它也可以很容易地使用流(streams)或 riverpodflutter_blocsignals 等包提供的其他库来编写。此应用层之间的通信全部通过方法调用处理,包括轮询新数据。它也可以使用流来从仓库向视图模型公开数据,并且仍然符合本指南中涵盖的规则。

即使您严格遵循本指南,并选择不引入额外的库,您仍需要做出决定:您会有一个领域层(domain layer)吗?如果会,您将如何管理数据访问?答案很大程度上取决于各个团队的需求,因此没有唯一正确的答案。无论您如何回答这些问题,本指南中的原则都将帮助您编写可扩展的 Flutter 应用。

如果您仔细看,难道所有架构最终不都是 MVVM 吗?

反馈

#

由于本网站的此部分正在不断完善中,我们欢迎您提供反馈