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

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

#

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

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

另一个例子是利用网络极低的分享门槛。如果您正在部署一个 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 并独立于设备运行位置验证行为。这也意味着,如果以后您决定在网上购买对于 Android 用户来说不是正确的流程,您可以更改实现,而可点击文本的测试无需更改。

功能

#

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

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

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

策略

#

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

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

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

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

RPC 支持的策略更改适用于增量功能发布或可能稍后更改的决策。

概述

#

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

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

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

根据它们试图分支的内容,而不是根据设备类型,命名功能和策略类中的方法。