使用 Flutter inspector 工具

这是什么?

#

Flutter widget inspector 是一个强大的工具,用于可视化和查看 widget 树。 Flutter 框架层使用 widgets 作为核心构建模块来处理从控件(例如文本、按钮和切换等)到布局(例如居中、填充、行和列等)的所有内容。 Flutter inspector 不仅可以帮助你可视化查看 Flutter widget 树,还有其他的作用:

  • 了解现有布局

  • 诊断布局问题

Screenshot of the Flutter inspector window

开始使用

#

要调试布局问题,请在 Debug 模式 下运行应用程序,然后点击 DevTools 工具栏上的 Flutter inspector 选项打开调试面板。

可视化地调试布局问题

#

下面是 Flutter inspector 工具栏中可用功能的指南。当空间有限时,将直接使用图标展示。

Select widget mode icon 选择 widget 模式
启用此按钮以在设备上选择 widget 进行查看。有关更多信息,请参考 查看 widget

Refresh tree icon 刷新树
重新加载当前 widget 的信息。

Slow animations icon 慢速动画
以五分之一的速度运行动画以便对它们进行优化。

Show guidelines mode icon 显示引导线
覆盖一层引导线以帮助调整布局问题。

Show baselines icon 显示基线
针对文字对齐展示文字的基线。对检查文字是否对齐有帮助。

Highlight repaints icon 高亮重绘制内容
元素重新绘制时,会依次显示不同颜色的边框。用于查找不必要的重绘。

Highlight oversized images icon 高亮尺寸过大的图片
在运行的应用程序中高亮并反转消耗过多内存的图像。

检查一个 widget

#

你可以浏览 widget 树并查看其附近的 widgets 和它们的属性值。

要在 widget 树中找到单个 UI 元素,请点击工具栏中的 Select Widget Mode 按钮。这将使设备上的应用程序进入「widget select」模式。点击应用界面上的任何 widget,将选中 widget 并将 widget 树滚动到对应的节点。再次点击 Select Widget Mode 按钮则退出「widget select」模式。

在调试布局问题时,要查看的关键字段是 sizeconstraints。其中约束沿树结构向下传递,尺寸信息则向上返回。想要了解更多信息,可以查看 深入理解 Flutter 布局约束

Flutter 布局浏览器

#

Flutter 布局浏览器可以帮助你更好地理解 Flutter 布局。

有关此工具的操作概述,观看 Flutter Explorer 的介绍视频:


DevTools Layout Explorer

下面详细介绍的文章可能对你有帮助:

使用布局浏览器

#

从 Flutter Inspector 中,选择一个 widget。布局浏览器支持 弹性布局 和固定大小的布局,并且针对它们配备了特定的工具。

弹性布局

#

当你选择了一个弹性布局 widget(例如,RowColumnFlex)或它的子 widget 时,弹性布局工具将显示在布局浏览器中。

布局浏览器会直观的显示 Flex widgets 及其子元素的布局方式。浏览器中还会显示主轴和交叉轴,以及每个轴当前的对齐方式(例如,start、end 和 spaceBetween)。它还显示了诸如弹性系数、弹性适配和布局约束等详细信息。

此外,浏览器中还会显示布局约束冲突和渲染溢出错误。正如你在设备上看到的那样,违背布局约束的地方会被标记成红色,溢出错误以标准的「黄色条带」显示。这些可视化的错误是为了让我们更好地理解溢出错误发生的原因,并了解如何修复它们。

The Layout Explorer showing errors and device inspector

Select Widget Mode 模式下,点击布局浏览器中的 widget 会同步选择到设备上。启用此模式,请点击调试面板中的 Select Widget Mode 按钮。

The Select Widget Mode button in the inspector

你可以在布局浏览器的下拉列表修改属性值,例如弹性系数、弹性适配和对齐方式。当修改 widget 的属性时,你会看到新的值同时在浏览器和运行 Flutter 应用程序的设备上生效。浏览器通过动画使更改的效果清晰可见。从布局浏览器中对 widget 属性的更改不会修改源代码,将在热重载时还原。

交互属性
#

布局资源管理器支持修改 mainAxisAlignmentcrossAxisAlignmentFlexParentData.flex。将来,我们可能会添加对其他属性的支持,例如 mainAxisSizetextDirectionFlexParentData.fit.

mainAxisAlignment
#

The Layout Explorer changing main axis alignment

支持属性:

  • MainAxisAlignment.start
  • MainAxisAlignment.end
  • MainAxisAlignment.center
  • MainAxisAlignment.spaceBetween
  • MainAxisAlignment.spaceAround
  • MainAxisAlignment.spaceEvenly
crossAxisAlignment
#

The Layout Explorer changing cross axis alignment

支持属性:

  • CrossAxisAlignment.start
  • CrossAxisAlignment.center
  • CrossAxisAlignment.end
  • CrossAxisAlignment.stretch
FlexParentData.flex
#

The Layout Explorer changing flex factor

布局浏览器支持设置 7 种弹性因子(null、0、1、2、3、4、5),但从技术上讲,弹性 widget 子级的弹性因子可以是任何整数。

Flexible.fit
#

The Layout Explorer changing fit

布局浏览器支持两种不同类型的 FlexFitloosetight

固定大小布局

#

当你选择一个固定大小的 widget 而不是弹性 widget 时,它的布局信息将显示在布局浏览器中。你可以看到所选 widget 及其最近的上一级 RenderObject 的大小、约束和填充信息。

The Layout Explorer fixed size tool

调试视觉效果

#

The Flutter Inspector provides several options for visually debugging your app.

Flutter Inspector 提供了多种以可视化方式调试应用的方式。以下是在 Flutter DevTools 中的 inspector 可用的选项:

慢速动画

#

启用时,动画将以约五分之一的原有速度运行,方便对视觉效果进行检查。当你想要仔细地观察并调试看起来不正常的动画时,这个选项会非常有用。

你也可以使用代码设置:

dart
import 'package:flutter/scheduler.dart';

void setSlowAnimations() {
  timeDilation = 5.0;
}

这会让动画时长增加 5 倍(速度减慢 5 倍)。

更多内容

#

以下的链接提供了更多细节内容。

以下的录屏展示了动画减速前后的对比。

Screen recording showing normal animation speed Screen recording showing slowed animation speed

显示引导线

#

该功能会在你的应用顶层绘制引导线,展示绘制区域、对齐、间距、滚动视图、裁剪和空位填充。

这个工具能帮助你更加了解你的布局。例如查找不需要的填充或者理解 widget 的对齐方式。

你也可以通过代码启用:

dart
import 'package:flutter/rendering.dart';

void showLayoutGuidelines() {
  debugPaintSizeEnabled = true;
}

Render boxes

#

RenderBox

#

绘制在屏幕上的 widgets 会创建一个 RenderBox,它是 Flutter 布局的基础构建。这些 RenderBox 会加上一个浅蓝色的边框:

Screenshot of render box guidelines

对齐方式

#

对齐方式将以黄色箭头展示。这些箭头会显示出垂直和竖屏方向上 widget 相对其父布局的偏移。例如,这个按钮图标有四个箭头表示它被居中展示:

Screenshot of alignment guidelines

间距

#

间距会以半透明的蓝色背景显示:

Screenshot of padding guidelines

滚动视图

#

包含滚动内容的 widget(例如 ListView)会展示绿色的箭头:

Screenshot of scroll view guidelines

裁剪

#

使用了诸如 ClipRect Widget 进行裁剪的内容,会以粉红色的虚线加一个剪刀图标展示:

Screenshot of clip guidelines

空位填充

#

空位填充的 widgets 会以灰色背景展示,例如没有 child 的 SizedBox

Screenshot of spacer guidelines

显示基线

#

该选项会显示所有的基线。基线是水平的用来定位文字的线。

在检查文字是否垂直对齐时,基线会非常有用。例如,下图中文字的基线稍微有一些错位:

Screenshot with show baselines enabled

Baseline widget 可以用来调整基线。

在设置了基线的 RenderBox 上,都会显示一条线。字母的基线以绿色展示,而符号的基线以黄色展示。

你也可以通过代码启用:

dart
import 'package:flutter/rendering.dart';

void showBaselines() {
  debugPaintBaselinesEnabled = true;
}

高亮重绘制内容

#

该选项会为所有的 RenderBox 绘制一层边框,在它们重新绘制时改变颜色。

以彩虹色谱循环的颜色,有利于你找到应用中频繁重绘导致性能消耗过大的部分。

例如,一个小动画可能会导致整个页面一直在重绘。将动画使用 RepaintBoundary widget 嵌套,可以保证动画只会导致其本身重绘。

下面是一个进度指示器导致其容器重绘的例子:

dart
class EverythingRepaintsPage extends StatelessWidget {
  const EverythingRepaintsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Repaint Example')),
      body: const Center(
        child: CircularProgressIndicator(),
      ),
    );
  }
}

Screen recording of a whole screen repainting

将进度指示器使用 RepaintBoundary 包裹,可以将重绘范围缩小至它本身占有的区域。

dart
class AreaRepaintsPage extends StatelessWidget {
  const AreaRepaintsPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Repaint Example')),
      body: const Center(
        child: RepaintBoundary(
          child: CircularProgressIndicator(),
        ),
      ),
    );
  }
}

Screen recording of a just a progress indicator repainting

RepaintBoundary widget 也有一些额外的消耗。它们对性能有一定的帮助,但也会在创建额外的绘制画布时增加一定的内存消耗。

你也可以通过代码启用:

dart
import 'package:flutter/rendering.dart';

void highlightRepaints() {
  debugRepaintRainbowEnabled = true;
}

高亮尺寸过大的图片

#

该选项会将尺寸过大的图片高亮表示,并且进行垂直翻转及色调反转:

A highlighted oversized image

被高亮的图片使用了过多的内存。例如一张 5MB 大小的图片以 100x100 像素展示。

这样的图片会导致性能低下,在低端设备上尤为明显,而当你在诸如列表中有大量这样的图片时,性能的下降会叠加。调试控制台窗口中会打印每个图片的信息:

dash.png has a display size of 213×392 but a decode size of 2130×392, which uses an additional 2542KB.

超过 128KB 的图片会被视为过大。

调整图片

#

在可能的情况下,最好的办法是调整图片资源的大小,让它变得更小。

如果该方法不可行,你可以使用 Image 构造里的 cacheHeightcacheWidth 参数:

dart
class ResizedImage extends StatelessWidget {
  const ResizedImage({super.key});

  @override
  Widget build(BuildContext context) {
    return Image.asset(
      'dash.png',
      cacheHeight: 213,
      cacheWidth: 392,
    );
  }
}

这样的方法可以让引擎以指定的大小解析图片,减少内存的消耗(解析开销和空间占用相较图片调整图片本身仍然较大)。无论如何设置参数,图片依然会以布局限制或大小进行渲染。

该属性同样可以使用代码设置:

dart
void showOversizedImages() {
  debugInvertOversizedImages = true;
}

更多内容

#

以下的链接提供了更多细节内容:

树的详细信息

#

选择 Widget Details Tree 标签来显示选中 widget 的详细信息树。从这里,你可以收集关于 widget 属性、渲染对象和子节点的有用信息。

The Details Tree view

追踪 widget 创建

#

Flutter inspector 的部分功能是基于检测应用程序的源码,以便更好地理解创建 widget 的源位置。 Flutter inspector 可以以类似于在源代码中定义 UI 的方式呈现 widget 树。如果没有它,widget 树中的组成某个节点的树结构会更深,并且更难理解运行时 widget 的层次结构如何与应用程序的 UI 相对应。

你可以通过在 flutter run 后面添加参数 -no track widget creation 来禁用此功能。

下面是 widget 树在启用和不启用追踪 widget 创建下的示例。

启用追踪 widget 创建(默认):

The widget tree with track widget creation enabled

关闭追踪 widget 创建(不推荐):

The widget tree with track widget creation disabled

此功能可避免在调试构建中将其他相同的 const 的 Widgets 视为相同。有关更多详细信息,请参阅关于 调试时常见问题 的讨论。

设置 Inspector

#

The Flutter Inspector Settings dialog

启用 hover 检测 (Enable hover inspection)

#

将鼠标悬停在任意 widget 上,会显示该 widget 的属性和值。

你可以切换 Enable hover inspection 启用或禁用 hover 检测功能。

Package 目录 (Package Directories)

#

默认情况下,DevTools 会根据项目根目录中的 widget 和 Flutter 中的 widget,限制 widget 树中的显示。这种过滤仅适用于 Inspector Widget Tree(Inspector 左侧)中的 widget -- 不适用于 Widget Details Tree (Inspector 右侧,与 Layout Explorer 处于同一选项卡视图中)。在 Widget Details Tree 中,你可以看见树中所有 package 的 widget。

要显示其他 widget,必须在 Package Directories 中添加它们的父目录。

例如,以下目录结构:

project_foo
  pkgs
    project_foo_app
    widgets_A
    widgets_B

project_foo_app 运行应用时,只会在 widget inspector 树中显示 project_foo/pkgs/project_foo_app 的 widget。

如果需要在 widget 树中显示 widgets_A 的 widget,请在 Package Directories 中添加 project_foo/pkgs/widgets_A

如果需要在 widget 树中显示项目根目录下的 所有 widget,请在 Package Directories 中添加 project_foo

你对 Package Directories 的更改是一直存在的,下次打开应用程序的 widget inspector 时依旧生效,

其他资源

#

有关 Flutter inspector 常用功能的演示,请参考 DartConf 2018 talk 中基于 IntelliJ 上的演示。

想要学习如何使用 DevTools 以可视化的方式调试布局问题,可以阅读 Flutter 布局检查器教程