跳到主内容

架构建议与资源

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

本页面介绍了架构最佳实践、它们的重要性,以及我们是否建议在你的 Flutter 应用中使用它们。请将这些建议视为参考而非死板的规则,你应该根据应用的具体需求进行适配。

本页面上的最佳实践设有优先级,反映了 Flutter 团队推荐它们的强烈程度。

  • 强烈推荐:如果你正在构建一个新应用,应该始终采用此建议。对于现有应用,除非实施该实践会与你当前的开发方式产生根本性冲突,否则应认真考虑进行重构以采用它。
  • 推荐:该实践很可能会改善你的应用。
  • 视情况而定:该实践在特定情况下可以改善你的应用。

关注点分离

#

你应该将应用拆分为 UI 层和数据层。在这些层级内,应进一步按照职责将逻辑拆分到不同的类中。

建议描述
使用明确定义的数据层和 UI 层。
强烈推荐

关注点分离是架构最重要的原则。数据层向应用的其余部分公开应用数据,并包含应用中大部分的业务逻辑。UI 层显示应用数据并监听用户的事件。UI 层包含用于 UI 逻辑和 Widget 的单独类。

在数据层使用存储库(Repository)模式。
强烈推荐

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

在 UI 层使用 ViewModel 和 View。(MVVM)
强烈推荐

关注点分离是架构最重要的原则。这种特定的分离方式使代码出错率大大降低,因为你的 Widget 保持为“哑(dumb)”组件。

使用 ChangeNotifierListenable 来处理 Widget 更新。
视情况而定

ChangeNotifier API 是 Flutter SDK 的一部分,是一种让 Widget 观察 ViewModel 变化的便捷方式。

有很多选项可以处理状态管理,最终的决定取决于个人喜好。阅读我们的 ChangeNotifier 建议其他热门选项

不要在 Widget 中编写逻辑。
强烈推荐

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

  • 简单的 if 语句,用于根据 ViewModel 中的标志或可空字段显示和隐藏 Widget
  • 依赖于 Widget 进行计算的动画逻辑
  • 基于设备信息(如屏幕尺寸或方向)的布局逻辑。
  • 简单的路由逻辑
使用领域(Domain)层。
视情况而定

只有当你的应用具有极其复杂的逻辑,导致 ViewModel 变得臃肿,或者你发现自己在 ViewModel 中重复编写逻辑时,才需要使用领域层。在超大型应用中,用例(Use-cases)很有用,但在大多数应用中,它们会增加不必要的开销。

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

处理数据

#

谨慎处理数据可以使代码更易于理解、减少错误,并防止创建格式错误或意外的数据。

建议描述
使用单向数据流。
强烈推荐

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

使用 Command 处理来自用户交互的事件。
推荐

Command 可以防止应用中的渲染错误,并标准化 UI 层向数据层发送事件的方式。阅读 架构案例研究 中关于 Command 的内容。

使用不可变(Immutable)数据模型。
强烈推荐

不可变数据对于确保必要的更改仅发生在正确的地方(通常是数据层或领域层)至关重要。由于不可变对象在创建后无法修改,因此必须创建新实例来反映更改。此过程可防止 UI 层中的意外更新,并支持清晰的单向数据流。

使用 freezed 或 built_value 生成不可变数据模型。
推荐

你可以使用软件包来帮助生成数据模型中的有用功能,例如 freezedbuilt_value。它们可以生成常见的模型方法,如 JSON 序列化/反序列化、深度相等性检查和复制方法。如果你有大量的模型,这些代码生成包可能会显著增加应用的构建时间。

创建单独的 API 模型和领域模型。
视情况而定

使用单独的模型会增加冗余,但能防止 ViewModel 和用例中出现复杂性。

用于大型应用。

应用结构

#

组织良好的代码既有益于应用本身的健康,也有益于参与开发该代码的团队。

建议描述
使用依赖注入。
强烈推荐

依赖注入可以防止应用拥有全局可访问的对象,从而降低代码的出错率。我们建议使用 provider 软件包来处理依赖注入。

使用 go_router 进行导航。
推荐

Go_router 是 90% 的 Flutter 应用的首选方案。有些特定的用例 go_router 无法解决,在这种情况下,你可以直接使用 Flutter Navigator API 或尝试 pub.dev 上的其他软件包。

使用标准化的类、文件和目录命名约定。
推荐

我们建议按照架构组件所代表的含义来命名类。例如,你可能会有以下类:

  • HomeViewModel
  • HomeScreen
  • UserRepository
  • ClientApiService

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

使用抽象 Repository 类
强烈推荐

Repository 类是应用中所有数据的真实来源,并促进与外部 API 的通信。创建抽象 Repository 类允许你创建不同的实现,这些实现可用于不同的应用环境,例如“开发”和“预发布”。

测试

#

良好的测试实践使应用更灵活。它还使得添加新逻辑和新 UI 变得简单且风险较低。

建议描述
分别测试和组合测试架构组件。
强烈推荐
  • 为每个 Service、Repository 和 ViewModel 类编写单元测试。这些测试应单独测试每个方法的逻辑。
  • 为 View 编写 Widget 测试。测试路由和依赖注入尤为重要。
创建测试用的模拟对象(Fake),并编写利用模拟对象的代码。
强烈推荐

模拟对象(Fake)并不关心给定方法的内部工作原理,它们更关心输入和输出。如果在编写应用代码时考虑到这一点,你将被迫编写具有明确输入和输出的模块化、轻量级的函数和类。

推荐资源

#
  • 代码与模板
    • Compass 应用源代码 - 一个功能齐全、健壮的 Flutter 应用的源代码,实现了许多这些建议。
    • very_good_cli - 由 Flutter 专家 Very Good Ventures 制作的 Flutter 应用模板。该模板生成了类似的应用程序结构。
  • 文档
    • Very Good Engineering 架构文档 - Very Good Engineering 是 VGV 推出的文档站点,包含技术文章、演示和开源项目。它包括有关架构 Flutter 应用的文档。
  • 工具
    • Flutter 开发者工具 (DevTools) - DevTools 是用于 Dart 和 Flutter 的一套性能和调试工具。
    • flutter_lints - 一个包含 Flutter 团队推荐的 Flutter 应用 Lint 规则的软件包。使用此软件包可以在团队中鼓励良好的编码实践。

反馈

#

由于本网站的这一部分正在不断完善中,我们欢迎你的反馈