跳到主内容

自适应应用通用方法

关于如何构建 Flutter 自适应应用的通用建议。

那么,究竟该如何着手将专为传统移动设备设计的应用,适配成在各种设备上都能完美呈现呢?需要哪些步骤?

拥有大型应用适配经验的 Google 工程师建议采取以下 3 个步骤。

第 1 步:抽象

#

Step 1: Abstract info common to any UI widget

首先,确定你计划动态化的组件。分析这些组件的构造函数,并将可以共享的数据抽象出来。

需要自适应的常见组件包括:

  • 对话框(全屏和模态)
  • 导航 UI(侧边栏和底部导航栏)
  • 自定义布局,例如“UI 区域是更高还是更宽?”

例如,在 Dialog 组件中,你可以共享包含对话框内容的信息。

又或者,你可能希望在应用窗口较小时切换到 NavigationBar,而在应用窗口较大时使用 NavigationRail。这些组件通常会共享一个可导航目标列表。在这种情况下,你可以创建一个 Destination 组件来保存此信息,并指定 Destination 同时包含图标和文本标签。

接下来,你需要评估屏幕尺寸,以决定如何显示你的 UI。

第 2 步:度量

#

Step 2: How to measure screen size

有两种确定显示区域大小的方法:MediaQueryLayoutBuilder

MediaQuery

#

过去,你可能使用过 MediaQuery.of 来确定设备屏幕的大小。然而,如今的设备屏幕尺寸和形状各异,仅凭此测试可能会产生误导。

例如,你的应用当前可能在大型屏幕上仅占据一个很小的窗口。如果你使用 MediaQuery.of 方法并判定屏幕较小(而实际上应用只是在大型屏幕的一个小窗口中显示),并且你锁定了应用的纵向模式,这会导致应用窗口被锁定在屏幕中央,周围留下黑边。这在大型屏幕上绝非理想的 UI 体验。

请记住,MediaQuery.sizeOf 返回的是应用整个屏幕的当前尺寸,而不仅仅是单个组件的尺寸。

你有两种度量屏幕空间的方法。你可以根据是需要整个应用窗口的尺寸,还是更局部的尺寸,来选择使用 MediaQuery.sizeOfLayoutBuilder

如果你希望组件全屏显示,即使应用窗口较小时也是如此,请使用 MediaQuery.sizeOf,这样你就可以根据应用窗口本身的尺寸来选择对应的 UI。在上一节中,由于你希望基于整个应用窗口来决定尺寸行为,因此应该使用 MediaQuery.sizeOf

build 方法中请求应用窗口尺寸(如 MediaQuery.sizeOf(context))会导致给定的 BuildContext 在 size 属性发生任何变化时重新构建。

LayoutBuilder

#

LayoutBuilder 实现了与 MediaQuery.sizeOf 相似的目标,但有一些区别。

LayoutBuilder 不会提供应用窗口的尺寸,而是提供来自父级 Widget 的布局约束。这意味着你获得的是基于你在 Widget 树中添加 LayoutBuilder 的特定位置的尺寸信息。此外,LayoutBuilder 返回的是 BoxConstraints 对象而不是 Size 对象,因此你得到的是内容的有效宽度和高度范围(最小值和最大值),而不仅仅是一个固定尺寸。这对于自定义组件非常有用。

例如,想象一个自定义组件,你希望其尺寸基于专门分配给该组件的空间,而不是整个应用窗口。在这种情况下,请使用 LayoutBuilder

第 3 步:分支

#

Step 3: Branch the code based on the desired UI

此时,你需要决定在选择显示哪个版本的 UI 时使用什么尺寸断点。例如,Material 布局指南建议,对于宽度小于 600 逻辑像素的窗口,使用底部导航栏;而对于 600 逻辑像素及以上的窗口,则使用导航侧边栏。同样,你的选择不应依赖于设备的类型,而应取决于设备可用的窗口大小。

要查看如何在 NavigationRailNavigationBar 之间切换的示例,请参阅 构建具有 Material 3 动效的响应式应用布局

下一页将讨论如何确保你的应用在大屏设备和折叠屏设备上看起来效果最佳。