跳转至正文

在 Flutter 与 Dart DevTools 中构建自定义工具

学习如何在 DevTools 中构建自定义开发者工具。

你是否曾想为 Dart 与 Flutter 构建开发者工具却不知从何入手?或者不想花费大量精力去连接正在运行的 Dart 或 Flutter 应用以访问调试数据?即便你创建了开发工具,又如何部署或让用户便捷访问?你可以绕过这些障碍来创建开发者工具。

借助 Dart 与 Flutter DevTools 扩展框架,你可以轻松构建与现有 DevTools 工具套件紧密集成的开发者工具。扩展使用 Flutter web 构建,并复用 DevTools 中的框架与工具,简化开发者工具的编写体验。

Example DevTools extension for package:foo

DevTools 扩展如何工作?

#

扩展作为 pub package 的一部分发布。你可以向现有 pub package 添加 DevTools 扩展,或创建仅提供 DevTools 扩展的新 package。在这两种情况下,最终用户必须依赖提供 DevTools 扩展的 package,才能在 DevTools 中看到该扩展。

例如,假设有 package:foo,该 package 提供 DevTools 扩展。当用户在应用中依赖 package:foo 时,会自动获得该 package 提供的 DevTools 扩展访问权限。当 DevTools 根据用户应用或 IDE 的信息检测到 package:foo 扩展可用时,会在 DevTools 中新增名为 "Foo" 的标签页,其中包含 package:foo 提供的开发者工具。

Diagram showing how a DevTools extension works

已在现有 package 中添加 DevTools 扩展的 package 示例包括 package:shared_preferencespackage:providerpackage:patrolpackage:drift

支持哪些类型的工具?

#

借助 DevTools 扩展框架,你可以构建多种工具,包括:

  • 现有 package 的配套工具。

  • 作为独立 package 发布的新工具。

  • 与正在运行的应用交互的工具。

  • 与 IDE 中打开的项目文件交互的工具。

  • 与分析服务器 (Analysis server) 交互的工具。

DevTools 扩展框架提供开箱即用的功能,使向用户分发扩展变得顺畅。例如,用户可以:

  • 在浏览器的 DevTools 中使用你的工具。

  • 在 IDE 中直接嵌入使用你的工具。

  • 从支持 Dart 与 Flutter 的 IDE 中发现并打开你的工具。

接下来,学习如何编写 DevTools 扩展。


编写 DevTools 扩展

#

开始之前,你需要:

  • Flutter SDK >= 3.17 与 Dart SDK >= 3.2。

  • 一个(在你看来)需要自定义 DevTools 扩展的 pub package。

设置 package 目录结构

#

你将提供独立扩展或配套扩展。

独立扩展

#

对于独立扩展(不作为现有 pub package 的一部分发布),扩展可与扩展同 package 包含源码,简化开发;由于用户会将你的 package 添加为 dev_dependency, package 体积不会影响用户应用大小。 package 结构如下所示:

yaml
my_new_tool
  extension/
    devtools/
      build/
        ...  # pre-compiled output of the Flutter web app under lib/
      config.yaml
  lib/  # source code for your extension Flutter web app
    src/
      ...

由于扩展以 Flutter web 应用形式构建,请使用 flutter create 生成 package:

flutter create --template app --platforms web my_new_tool

接下来,在下一步中使用 my_new_tool package 配置扩展。

配套扩展

#

对于作为现有 pub package 一部分发布的配套扩展,建议将扩展源码放在 pub package 之外,以尽量减小 package 体积,避免增大依赖该 package 的用户应用体积。推荐 package 结构如下:

yaml
foo/  # formerly the repository root of your pub package
  packages/
    foo/  # your pub package
      extension/
        devtools/
          build/
            ...  # pre-compiled output of foo_devtools_extension/lib
          config.yaml
    foo_devtools_extension/
      lib/  # source code for your extension Flutter web app

配置扩展

#

在向用户提供 DevTools 扩展的 Dart package 中,添加顶层 extension 目录:

yaml
foo/
  extension/
  lib/
  ...

extension 目录下, 完全按所示 创建以下结构:

yaml
extension/
  devtools/
    build/
    config.yaml

config.yaml 文件包含 DevTools 加载扩展所需的元数据:

yaml
name: foo
version: 0.0.1
issueTracker: <link_to_your_issue_tracker.com>
materialIconCodePoint: '0xe0b1'
requiresConnection: true  # optional field - defaults to true

按所示复制 config.yaml 内容,粘贴到你刚在 package 中创建的 config.yaml 文件中。 务必使用所示的确切文件名和字段名,否则扩展可能无法在 DevTools 中加载

为每个键填写适合你 package 的值。

  • name:此 DevTools 扩展的 package 名。该字段值用于扩展页面标题栏。[required](必填)

  • version:DevTools 扩展的版本号。随你发布新功能应逐步演进。该字段值用于扩展页面标题栏。[required](必填)

  • issueTracker:问题跟踪器 URL。用户在 DevTools UI 中点击 Report an issue(报告问题)链接时会跳转到此 URL。 [required](必填)

DevTools extension screen title bar

  • materialIconCodePoint:对应 material/icons.dart 中图标的 codepoint 值。该图标用于顶层 DevTools 标签栏中的扩展标签。[required](必填)

DevTools extension tab icon

  • requiresConnection:指示扩展是否需要已连接的 Dart 或 Flutter 应用才能使用。此为可选字段,未指定时默认为 true。[optional](可选)

有关 config.yaml 规范的最新文档,请访问 extension_config_spec.md

构建扩展

#

按以下步骤构建扩展。

创建 Flutter web 应用

#

在希望放置扩展源码的目录中运行以下命令,将 foo_devtools_extension 替换为 <your_package_name>_devtools_extension

flutter create --template app --platforms web foo_devtools_extension

添加 package:devtools_extensions 依赖

#
flutter pub add devtools_extensions

你可能还需要依赖 package:devtools_app_shared,其中包含构建扩展时可用的共享服务、工具与 UI 组件。示例用法见 devtools_app_shared/example

flutter pub add devtools_app_shared

添加 DevToolsExtension widget

#

lib/main.dart 中添加以下导入:

dart
import 'package:devtools_extensions/devtools_extensions.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const FooDevToolsExtension());
}

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

  @override
  Widget build(BuildContext context) {
    return const DevToolsExtension(
      child: Placeholder(), // Build your extension here
    );
  }
}

DevToolsExtension widget 会自动初始化与 DevTools 交互所需的全部扩展。在扩展 web 应用的任意位置,可访问以下全局对象:

  • extensionManager:用于与 DevTools 或扩展框架交互的管理器。

  • serviceManager:用于与已连接的 vm service 交互的管理器(若存在)。

  • dtdManager:用于与 Dart Tooling Daemon 交互的管理器(若存在)。

调试扩展

#

开发与维护 DevTools 扩展时,你需要运行、调试和测试扩展的 Flutter web 应用。下面概述了几种可选方式。

#

调试时,你可能希望使用「simulated DevTools environment」(模拟 DevTools 环境)。该模拟环境让你在不将扩展作为 DevTools 内嵌 iFrame 开发的情况下构建扩展。以此方式运行会用模拟 DevTools 与扩展连接的环境包裹扩展,并提供热重启与更快的开发周期。

Debugging an extension with the Simulated DevTools Environment

  1. 你的 DevTools 扩展。

  2. 扩展将与之交互的测试应用的 VM service URI。该应用应依赖扩展的父 package(本例为 package:foo)。

  3. 用于执行用户可能从 DevTools 触发的操作的按钮。

  4. 显示扩展与 DevTools 之间将发送消息的日志。

模拟环境由环境参数 use_simulated_environment 启用。要在启用该标志的情况下运行扩展 web 应用,请在 VS Code 的 launch.json 中添加配置:

json
{
    ...
    "configurations": [
        ...
        {
            "name": "foo_devtools_extension + simulated environment",
            "cwd": "packages/foo_devtools_extension",
            "request": "launch",
            "type": "dart",
            "args": [
                "--dart-define=use_simulated_environment=true"
            ],
        },
    ]
}

或在命令行带该标志启动应用:

flutter run -d chrome -dart-define=use_simulated_environment=true

选项 B:使用真实 DevTools 环境

#

当扩展开发到可在真实 DevTools 环境中测试变更的阶段时,需要执行一系列设置步骤:

  1. Develop your extension to a point where you are ready to test your changes in a real DevTools environment. Build your flutter web app and copy the built assets from `your_extension_web_app/build/web` to your pub package's `extension/devtools/build directory`.

    将扩展开发到可在真实 DevTools 环境中测试变更的阶段。构建 Flutter web 应用,并将构建产物从 your_extension_web_app/build/web 复制到 pub package 的 extension/devtools/build 目录。

    可使用 package:devtools_extensionsbuild_and_copy 命令完成此步。

    console cd your_extension_web_app; flutter pub get; dart run devtools_extensions build_and_copy --source=. --dest=path/to/your_pub_package/extension/devtools

    为确保扩展在 DevTools 中正确加载,请运行 package:devtools_extensionsvalidate 命令。 --package 参数应指向将与此扩展一起发布的 Dart package 根目录。

    console cd your_extension_web_app; flutter pub get; dart run devtools_extensions validate --package=path/to/your_pub_package

  2. Prepare a test environment with a dependency on your pub package that is providing the extension.

    准备测试环境,并依赖提供该扩展的 pub package。

    在要添加对你 package 依赖的 Dart 或 Flutter 项目中,添加指向本地 package 源码的 path 依赖(包含带扩展资源的 extension/devtools/ 目录的 package)。完成后在该 package 上运行 pub get

    若扩展需要正在运行的应用,则需运行依赖该扩展的应用。

    若扩展不需要正在运行的应用,则需在支持的 IDE(VS Code 或 IntelliJ / Android Studio)中打开依赖你 package 的测试 Dart 或 Flutter 项目。

  3. Start DevTools

    启动 DevTools

    可使用以下任一方式启动 DevTools:

    若扩展需要正在运行的应用,可从运行测试应用时命令行打印的 URI 打开 DevTools,或从运行测试应用的 IDE 打开。

    若扩展不需要正在运行的应用,可在支持的 IDE(VS Code 或 IntelliJ / Android Studio)中打开依赖你 package 的 Dart 或 Flutter 项目,从 IDE 打开 DevTools 在浏览器中查看扩展。

    若需要 DevTools 的本地或未发布变更,需从源码构建并运行 DevTools。请参阅 DevTools CONTRIBUTING.md。需同时构建服务器与前端以测试扩展(instructions)。

  4. Connect your test app to DevTools if it is not connected already, and you should see a tab in the DevTools app bar for your extension. The enabled or disabled state of your extension is managed by DevTools, which is exposed from an **Extensions** menu in DevTools, available from the action buttons in the upper right corner of the screen.

    若测试应用尚未连接,请将其连接到 DevTools,你应能在 DevTools 应用栏中看到扩展标签页。扩展的启用/禁用状态由 DevTools 管理,可通过屏幕右上角操作按钮中的 Extensions(扩展)菜单访问。

打开 DevTools 后,应用栏中应出现扩展标签页。扩展的启用/禁用状态由 DevTools 管理,可通过屏幕右上角操作按钮中的 "Extensions"(扩展)菜单访问。

DevTools Extensions menu button

DevTools Extensions menu

发布带有 DevTools 扩展的 package

#

package 要向用户提供 DevTools 扩展,必须在 your_pub_package/extension/devtools/ 目录中包含预期内容后发布(如前述设置说明所述)。

  1. Ensure the `extension/devtools/config.yaml` file exists and is configured per the specifications above. You can run the `validate` command from `package:devtools_extensions` to verify.

    确保 extension/devtools/config.yaml 存在且按上述规范配置。可运行 package:devtools_extensionsvalidate 命令验证。

    cd your_extension_web_app;
    flutter pub get;
    dart run devtools_extensions validate --package=path/to/pkg_providing_your_extension_assets
    
  2. Use the `build_and_copy` command provided by `package:devtools_extensions` to build your extension and copy the output to the `extension/devtools` directory:

    使用 package:devtools_extensions 提供的 build_and_copy 命令构建扩展并将输出复制到 extension/devtools 目录:

    console cd your_extension_web_app; flutter pub get; dart run devtools_extensions build_and_copy --source=. --dest=path/to/your_pub_package/extension/devtools

然后在 pub.dev 上发布 package:

flutter pub publish

运行 pub publish 时,若缺少必需的 config.yaml 或非空的 build 目录,会看到警告。

有关发布 package 的更多说明,请参阅 package:devtools_extensions发布指南


就是这样!当用户依赖你 package 的最新版本时,会自动获得你在 DevTools 扩展中提供的工具。

以下链接可能对你有帮助: