大多数真实世界的应用程序都需要适应不同设备和平台的功能和策略。本页包含有关如何在代码中处理这些场景的建议。

根据每种设备类型的优势进行设计

#

考虑不同设备的独特优势和劣势。除了它们的屏幕尺寸和输入(如触摸、鼠标、键盘)之外,您还可以利用哪些其他独特功能?Flutter 让您的代码能够在不同的设备上运行,但强大的设计不仅仅是运行代码。思考每个平台最擅长什么,看看是否有独特的优势可以利用。

例如:Apple 的 App Store 和 Google 的 Play Store 有不同的应用遵守规则。不同的主机操作系统在不同时期以及彼此之间也具有不同的功能。

另一个例子是利用 Web 极低的共享门槛。如果您正在部署 Web 应用程序,请决定要支持哪些深层链接,并在设计导航路由时考虑到它们。

Flutter 推荐的基于这些独特功能处理不同行为的模式是为您的应用创建一组 CapabilityPolicy 类。

功能

#

功能(capability)定义了代码或设备能够做什么。功能的例子包括:

  • API 的存在
  • 操作系统强制的限制
  • 物理硬件要求(如摄像头)

策略

#

策略(policy)定义了代码应该做什么。

策略的例子包括:

  • 应用商店指南
  • 设计偏好
  • 引用主机设备的资产或文案
  • 服务器端启用的功能

如何组织策略代码

#

最简单的机械方式是使用 Platform.isAndroidPlatform.isIOSkIsWeb。这些 API 机械地告诉您代码在哪里运行,但随着应用程序运行范围的扩展以及主机平台添加功能,它们会存在一些问题。

以下指南解释了开发应用程序功能和策略时的最佳实践:

避免使用 Platform.isAndroid 和类似函数来做出布局决策或对设备能做什么进行假设。

相反,在方法中描述您想要分支的条件。

示例:您的应用有一个在网站上购买商品的链接,但由于政策原因,您不希望在 iOS 设备上显示该链接。

dart
bool shouldAllowPurchaseClick() {
  // Banned by Apple App Store guidelines. 
  return !Platform.isIOS;
}

...
TextSpan(
  text: 'Buy in browser',
  style: new TextStyle(color: Colors.blue),
  recognizer: shouldAllowPurchaseClick ? TapGestureRecognizer()
    ..onTap = () { launch('<some url>') : null;
  } : null,

通过增加一层间接性您获得了什么?代码更清晰地说明了分支路径存在的原因。此方法可以直接存在于类中,但代码的其他部分可能需要相同的检查。如果是这样,请将代码放入一个类中。

policy.dart
dart

class Policy {

  bool shouldAllowPurchaseClick() {
    // Banned by Apple App Store guidelines. 
    return !Platform.isIOS;
  }
}

将此代码放在一个类中,任何 widget 测试都可以模拟 Policy().shouldAllowPurchaseClick 并独立于设备运行位置验证行为。这也意味着,如果以后您决定在 Web 上购买不适合 Android 用户,您可以更改实现,而可点击文本的测试无需更改。

功能

#

有时您希望代码执行某些操作,但 API 不存在,或者您依赖的插件功能尚未在所有您支持的平台上实现。这是设备能够做什么的限制。

这些情况类似于上述的策略决策,但它们被称为功能(capabilities)。为什么在类的结构相似的情况下将策略类与功能分离?Flutter 团队发现,对于已投入生产的应用程序,在应用程序能够做什么和它们应该做什么之间做出逻辑区分,有助于大型产品在初始代码编写后,除了您自己的偏好之外,还能应对平台功能或要求的变化。

例如,考虑一种情况,某个平台添加了一个新的权限,要求用户在您的代码调用敏感 API 之前与系统对话框进行交互。您的团队为平台 1 完成了这项工作,并创建了一个名为 requirePermissionDialogFlow 的功能。然后,如果平台 2 也添加了类似的要求,但仅限于新的 API 版本,那么 requirePermissionDialogFlow 的实现现在可以检查 API 级别并为平台 2 返回 true。您已经利用了您已经完成的工作。

策略

#

我们鼓励您最初从一个 Policy 类开始,即使您觉得不会做出很多基于策略的决策。随着类的复杂性增加或输入数量扩展,您可能会决定按功能或其他标准拆分策略类。

对于策略实现,您可以使用编译时、运行时或远程过程调用 (RPC) 支持的实现。

编译时策略检查适用于偏好 unlikely 会改变且意外改变值可能导致严重后果的平台。例如,如果平台要求您不能链接到 Play 商店,或者根据您的应用内容要求您使用特定的支付提供商。

运行时检查对于确定用户是否可以使用触摸屏很有用。Android 有一个可以检查的功能,您的 Web 实现可以检查最大触摸点。

RPC 支持的策略更改适用于增量功能推出或以后可能更改的决策。

概述

#

使用 Capability 类来定义代码能够做什么。您可以检查 API 的存在、操作系统强制的限制以及物理硬件要求(如摄像头)。功能通常涉及编译或运行时检查。

使用 Policy 类(或根据复杂性使用多个类)来定义代码应该做什么以遵守应用商店指南、设计偏好以及需要引用主机设备的资产或文案。策略可以是编译时、运行时或 RPC 检查的混合。

通过模拟功能和策略来测试分支代码,这样当功能或策略改变时,widget 测试就不需要改变。

根据您试图分支的目的,而不是根据设备类型来命名您的功能和策略类中的方法。