跳转至正文

架构建议与资源

构建可扩展 Flutter 应用的建议。

本页介绍架构最佳实践、其重要性,以及是否建议你在 Flutter 应用中采用。请将这些内容视为建议而非铁律,并根据应用的独特需求加以调整。

本页最佳实践带有优先级,反映 Flutter 团队的推荐力度。

  • 强烈推荐: 若你正在新建应用,应始终落实该建议。除非与现有做法根本冲突,否则应认真考虑在既有应用中重构以落实该实践。

  • 推荐: 该实践很可能改善你的应用。

  • 视情况而定: 该实践在特定情形下可能改善你的应用。

关注点分离

#

你应将应用划分为 UI 层与数据层。在各层内部,还应按职责将逻辑进一步拆分到不同类中。

RecommendationDescription
使用清晰定义的数据层和 UI 层。
Strongly recommend

关注点分离是最重要的架构原则。数据层向应用的其余部分暴露应用数据,并包含应用中的大部分业务逻辑。 UI 层负责展示应用数据,并监听来自用户的事件。UI 层为 UI 逻辑和 widget 分别使用独立的类。

在数据层中使用仓库模式 (repository pattern)。
Strongly recommend

仓库模式是一种软件设计模式,它将数据访问逻辑与应用的其余部分隔离开来。它在应用的业务逻辑与底层数据存储机制(数据库、API、文件系统等)之间创建了一个抽象层。在实践中,这意味着创建 Repository 类和 Service 类。

在 UI 层中使用 ViewModel 和 View。(MVVM)
Strongly recommend

关注点分离是最重要的架构原则。这种特定的分离能大幅降低代码出错的概率,因为你的 widget 始终保持「被动」(即不含逻辑的 dumb widget)。

使用 ChangeNotifierListenable 处理 widget 更新。
Conditional

ChangeNotifier API 是 Flutter SDK 的一部分,它提供了一种便捷的方式,让你的 widget 观察 ViewModel 中的变化。

处理状态管理的选项很多,最终的决定取决于个人偏好。阅读 我们对 ChangeNotifier 的推荐其他流行选项

不要在 widget 中放置逻辑。
Strongly recommend

逻辑应当封装在 ViewModel 的方法中。View 唯一应当包含的逻辑是:

  • 根据 ViewModel 中的标志位或可空字段来显示或隐藏 widget 的简单 if 语句
  • 依赖 widget 才能完成计算的动画逻辑
  • 基于设备信息(如屏幕尺寸或方向)的布局逻辑
  • 简单的路由逻辑
使用领域层 (domain layer)。
Conditional

只有当你的应用拥有极其复杂的逻辑、以致 ViewModel 变得臃肿,或者你发现自己在多个 ViewModel 中重复编写相同逻辑时,才需要领域层。在超大型应用中,use-case(用例)很有用,但对大多数应用而言,它们只会带来不必要的开销。

适用于具有复杂逻辑需求的应用。

数据处理

#

谨慎处理数据能让代码更易理解、更少出错,并避免产生畸形或意外的数据。

RecommendationDescription
使用单向数据流。
Strongly recommend

数据更新应当只从数据层流向 UI 层。 UI 层中的交互会被发送到数据层进行处理。

使用 Commands 处理来自用户交互的事件。
Recommend

Command 能防止应用出现渲染错误,并统一 UI 层向数据层发送事件的方式。你可以在 架构案例研究 中进一步了解 Command。

使用不可变的数据模型。
Strongly recommend

不可变数据对于确保任何必要的变更只发生在恰当的位置(通常是数据层或领域层)而言至关重要。由于不可变对象在创建后无法被修改,你必须创建一个新的实例来反映变更。这一过程可以防止 UI 层中的意外更新,并支持清晰的单向数据流。

使用 freezed 或 built_value 生成不可变的数据模型。
Recommend

你可以借助一些 package 来为数据模型生成实用的功能,比如 freezedbuilt_value。它们可以生成常见的模型方法,例如 JSON 序列化/反序列化、深度相等性检查以及 copy 方法。如果你的模型数量众多,这些代码生成 package 可能会显著增加应用的构建时间。

创建相互独立的 API 模型 (API model) 和领域模型 (domain model)。
Conditional

使用相互独立的模型会增加一些冗余代码,但能避免 ViewModel 和 use-case 变得复杂。

适用于大型应用

应用结构

#

组织良好的代码既有利于应用本身的健康,也有利于维护代码的团队。

RecommendationDescription
使用依赖注入。
Strongly recommend

依赖注入可以避免应用中出现可全局访问的对象,从而降低代码出错的概率。我们推荐你使用 provider package 来处理依赖注入。

使用 go_router 进行导航。
Recommend

对于 90% 的 Flutter 应用,go_router 都是首选的编写方式。 go_router 无法解决某些特定的使用场景,这种情况下,你可以直接使用 Flutter Navigator API,或者尝试 pub.dev 上的其他 package。

为类、文件和目录使用统一的命名规范。
Recommend

我们推荐根据类所代表的架构组件来为其命名。例如,你可能会有以下这些类:

  • HomeViewModel
  • HomeScreen
  • UserRepository
  • ClientApiService

为了清晰起见,我们不推荐使用容易与 Flutter SDK 中的对象混淆的名称。例如,你应当把共享的 widget 放在名为 ui/core/ 的目录中,而不是放在名为 /widgets 的目录中。

使用抽象的 Repository 类
Strongly recommend

Repository 类是应用中所有数据的可信来源 (source of truth),并负责与外部 API 进行通信。创建抽象的 Repository 类让你可以编写不同的实现,以适配不同的应用环境,例如 development(开发)和 staging(预发布)。

测试

#

良好的测试实践使应用更灵活,也让新增逻辑与 UI 变得直接且低风险。

RecommendationDescription
为测试创建 fake 对象(并编写能充分利用 fake 的代码)。
Strongly recommend

fake 对象关注的并不是某个方法的内部实现,而是它的输入和输出。如果你在编写应用代码时牢记这一点,就会促使自己写出模块化、轻量的函数和类,并具备定义良好的输入与输出。

推荐资源

#
  • 代码与模板

    • Compass 应用源代码 — 功能完整、健壮的 Flutter 应用源代码,落实了本指南中的许多建议。

    • very_good_cli — 由 Flutter 专家 Very Good Ventures 制作的 Flutter 应用模板,可生成类似的应用结构。

  • 文档

    • Very Good Engineering 架构文档 — Very Good Engineering 是 VGV 的文档站点,包含技术文章、演示与开源项目,其中包括 Flutter 应用架构相关文档。

  • 工具

    • Flutter 开发者工具 — DevTools 是一套面向 Dart 与 Flutter 的性能与调试工具。

    • flutter_lints — 包含 Flutter 团队为 Flutter 应用推荐的 lint 规则的软件包,可用于在团队中推广良好编码实践。

反馈

#

网站本节内容仍在完善中, 欢迎提供反馈