跳到主内容

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

你是否曾想为 Dart 和 Flutter 构建开发者工具,却不知道从何入手?或者,你是否不想为了获取调试数据而投入大量精力去建立与运行中的 Dart 或 Flutter 应用的连接?即使你创建了一个开发工具,又该如何部署它或让用户轻松访问它呢?其实,你可以无需克服这些障碍来创建开发者工具。

借助 Dart 和 Flutter DevTools 扩展框架,你可以轻松构建与现有 DevTools 工具套件紧密集成的开发者工具。扩展使用 Flutter Web 构建,并利用 DevTools 现有的框架和实用程序,从而简化了开发者工具的创作体验。

Example DevTools extension for package:foo

DevTools 扩展是如何工作的?

#

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

例如,假设我们有一个 package:foo,它提供了一个 DevTools 扩展。当用户在他们的应用中依赖 package:foo 时,他们会自动获得该包提供的 DevTools 扩展的使用权限。当 DevTools 根据用户应用或 IDE 中的信息检测到 package:foo 扩展可用时,会在 DevTools 中添加一个名为“Foo”的新标签页,其中包含 package:foo 提供的开发者工具。

Diagram showing how a DevTools extension works

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

支持哪些类型的工具?

#

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

  • 现有包的配套工具。
  • 作为独立包发布的新工具。
  • 与运行中的应用程序交互的工具。
  • 与 IDE 中打开的项目文件交互的工具。
  • 与分析服务器(Analysis server)交互的工具。

DevTools 扩展框架自带开箱即用的功能,使向用户分发扩展变得无缝。例如,用户可以:

  • 在浏览器中的 DevTools 中使用你的工具。
  • 直接在 IDE 中嵌入使用你的工具。
  • 从支持 Dart 和 Flutter 的 IDE 中发现并打开你的工具。

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


编写 DevTools 扩展

#

开始之前,你需要:

  • Flutter SDK >= 3.17 和 Dart SDK >= 3.2。
  • 一个你认为需要自定义 DevTools 扩展的 pub 包。

设置你的包层次结构

#

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

独立扩展

#

对于独立扩展(非作为现有 pub 包的一部分发布),你的扩展可以将源代码包含在与扩展一同发布的同一个包中。这简化了开发过程,而且由于你的包用户会将你的包添加为 dev_dependency,因此包的大小不会影响用户的应用大小。你的包结构如下所示:

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 生成该包:

flutter create --template app --platforms web my_new_tool

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

配套扩展

#

对于配套扩展(作为现有 pub 包的一部分发布),建议将你的扩展源代码放在 pub 包之外。这可以尽可能减小包的大小,避免增加依赖你包的用户的应用体积。以下是推荐的包结构:

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 包中,添加一个顶级 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 文件,并粘贴到你刚刚在包中创建的 config.yaml 文件中。必须严格使用所示的文件名和字段名,否则你的扩展可能无法在 DevTools 中加载

对于每个键,请填写适用于你包的相应值。

  • name:此 DevTools 扩展的包名称。该字段的值用于扩展页面标题栏。[必需]
  • version:你的 DevTools 扩展版本。当你为扩展发布新功能时,此版本号应随之更新。该字段的值用于扩展页面标题栏。[必需]
  • issueTracker:你的问题追踪器 URL。当用户点击 DevTools UI 中的 Report an issue 链接时,他们将被引导至此 URL。[必需]

DevTools extension screen title bar

  • materialIconCodePoint:对应 material/icons.dart 中图标的码点值。此图标用于顶部 DevTools 标签栏中扩展的标签页。[必需]

DevTools extension tab icon

  • requiresConnection:指示扩展是否需要连接的 Dart 或 Flutter 应用才能使用。这是一个可选字段,如果不指定,默认为 true。[可选]

有关 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 组件

#

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 组件会自动初始化与 DevTools 交互所需的所有扩展。在你的扩展 Web 应用中的任何位置,你都可以访问以下全局变量:

  • extensionManager:用于与 DevTools 或扩展框架交互的管理器。
  • serviceManager:用于与已连接的 VM 服务(如果存在)交互的管理器。
  • dtdManager:用于与 Dart 工具守护进程(Dart Tooling Daemon,如果存在)交互的管理器。

调试你的扩展

#

在开发和维护 DevTools 扩展时,你需要运行、调试和测试你的扩展 Flutter Web 应用。你有几种不同的选项,如下所述:

#

出于调试目的,你可能需要使用“模拟 DevTools 环境”。这是一个模拟环境,允许你在无需将其作为嵌入式 iFrame 开发的情况下构建扩展。以这种方式运行扩展会将你的扩展包裹在一个模拟 DevTools 与 DevTools 扩展连接的环境中。它还使你可以访问热重启(hot restart)并获得更快的开发周期。

Debugging an extension with the Simulated DevTools Environment

  1. 你的 DevTools 扩展。
  2. 你的 DevTools 扩展将与之交互的测试应用的 VM 服务 URI。该应用应依赖于你扩展的父包(本例中为 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. 将扩展开发到准备在真实 DevTools 环境中测试更改的程度。构建你的 Flutter Web 应用,并将构建的资源从 `your_extension_web_app/build/web` 复制到你的 pub 包的 `extension/devtools/build` 目录中。

    使用 package:devtools_extensions 提供的 build_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_extensions 中的 validate 命令。--package 参数应指向该扩展将随之发布的 Dart 包的根目录。

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

  2. 准备一个测试环境,该环境依赖于提供扩展的 pub 包。

    在添加了对你包的依赖的 Dart 或 Flutter 项目中,添加一个指向你本地包源代码(包含 extension/devtools/ 目录及其资源的包)的 path 依赖。完成此操作后,在该包上运行 pub get

    * 如果你的扩展需要运行中的应用程序,那么你需要运行依赖于你的扩展的应用程序。 * 如果你的扩展不需要运行中的应用程序,那么你需要在支持的 IDE(VS Code 或 IntelliJ / Android Studio)中打开依赖于你的包的测试 Dart 或 Flutter 项目。

  3. 启动 DevTools

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

    * 如果你的扩展需要运行中的应用程序,你可以通过运行测试应用时打印到命令行的 URI,或者从运行测试应用的 IDE 中打开 DevTools。 * 如果你的扩展不需要运行中的应用程序,你可以在支持的 IDE(VS Code 或 IntelliJ / Android Studio)中打开依赖于你的包的 Dart 或 Flutter 项目。从 IDE 打开 DevTools 以在浏览器中查看你的扩展。 * 如果你需要来自 DevTools 的本地或未发布的更改,则需要从源代码构建并运行 DevTools。有关操作指南,请参阅 DevTools CONTRIBUTING.md。你需要构建带有服务器和前端的 DevTools 才能测试扩展(说明)。

  4. 如果你的测试应用尚未连接到 DevTools,请将其连接,你应该会在 DevTools 应用栏中看到你的扩展标签页。扩展的启用或禁用状态由 DevTools 管理,可通过屏幕右上角操作按钮中的 **Extensions** 菜单进行设置。

打开 DevTools 后,DevTools 应用栏中应该会出现一个扩展标签页。扩展的启用或禁用状态由 DevTools 管理,可通过屏幕右上角操作按钮中提供的“Extensions”菜单进行访问。

DevTools Extensions menu button

DevTools Extensions menu

发布带有 DevTools 扩展的包

#

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

  1. 确保 extension/devtools/config.yaml 文件存在并已根据上述规范进行配置。你可以运行 package:devtools_extensions 中的 validate 命令进行验证。
    cd your_extension_web_app;
    flutter pub get;
    dart run devtools_extensions validate --package=path/to/pkg_providing_your_extension_assets
    
  2. 使用 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 上发布你的包。

flutter pub publish

运行 pub publish 时,如果缺少 config.yaml 文件或 build 目录为空(这是必需的),你将看到警告。

有关发布包的其他指导,请访问 package:devtools_extensions发布指南


就是这样!现在,当用户依赖你的包的最新版本时,他们将自动获得你通过 DevTools 扩展提供的工具的使用权限。

你可能会觉得以下链接很有用: