在 Flutter 和 Dart DevTools 中构建自定义工具
你是否曾想为 Dart 和 Flutter 构建开发者工具,却不知道从何入手?或者,你是否不想为了获取调试数据而投入大量精力去建立与运行中的 Dart 或 Flutter 应用的连接?即使你创建了一个开发工具,又该如何部署它或让用户轻松访问它呢?其实,你可以无需克服这些障碍来创建开发者工具。
借助 Dart 和 Flutter DevTools 扩展框架,你可以轻松构建与现有 DevTools 工具套件紧密集成的开发者工具。扩展使用 Flutter Web 构建,并利用 DevTools 现有的框架和实用程序,从而简化了开发者工具的创作体验。
DevTools 扩展是如何工作的?
#扩展作为 pub 包的一部分发布。你可以向现有的 pub 包添加 DevTools 扩展,也可以创建一个仅提供 DevTools 扩展的新包。在这两种情况下,最终用户必须在依赖列表中包含提供该 DevTools 扩展的包,才能在 DevTools 中看到该扩展。
例如,假设我们有一个 package:foo,它提供了一个 DevTools 扩展。当用户在他们的应用中依赖 package:foo 时,他们会自动获得该包提供的 DevTools 扩展的使用权限。当 DevTools 根据用户应用或 IDE 中的信息检测到 package:foo 扩展可用时,会在 DevTools 中添加一个名为“Foo”的新标签页,其中包含 package:foo 提供的开发者工具。
已向现有包添加 DevTools 扩展的包示例包括 package:shared_preferences、package:provider、package:patrol 和 package: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,因此包的大小不会影响用户的应用大小。你的包结构如下所示:
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 包之外。这可以尽可能减小包的大小,避免增加依赖你包的用户的应用体积。以下是推荐的包结构:
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 目录:
foo/
extension/
lib/
...
在 extension 目录下,创建完全如下所示的结构:
extension/
devtools/
build/
config.yaml
config.yaml 文件包含 DevTools 加载扩展所需的元数据。
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。[必需]
-
materialIconCodePoint:对应material/icons.dart中图标的码点值。此图标用于顶部 DevTools 标签栏中扩展的标签页。[必需]
-
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 中,添加以下导入:
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 应用。你有几种不同的选项,如下所述:
选项 A:使用模拟的 DevTools 环境(推荐用于开发)
#出于调试目的,你可能需要使用“模拟 DevTools 环境”。这是一个模拟环境,允许你在无需将其作为嵌入式 iFrame 开发的情况下构建扩展。以这种方式运行扩展会将你的扩展包裹在一个模拟 DevTools 与 DevTools 扩展连接的环境中。它还使你可以访问热重启(hot restart)并获得更快的开发周期。
- 你的 DevTools 扩展。
-
你的 DevTools 扩展将与之交互的测试应用的 VM 服务 URI。该应用应依赖于你扩展的父包(本例中为
package:foo)。 - 用于执行用户可能从 DevTools 触发的操作的按钮。
- 显示将在你的扩展和 DevTools 之间发送的消息的日志。
模拟环境由环境变量 use_simulated_environment 启用。要在此标志开启的情况下运行你的扩展 Web 应用,请在 VS Code 的 launch.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 环境中测试更改的程度,你需要执行一系列设置步骤:
- 将扩展开发到准备在真实 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 - 准备一个测试环境,该环境依赖于提供扩展的 pub 包。
在添加了对你包的依赖的 Dart 或 Flutter 项目中,添加一个指向你本地包源代码(包含
extension/devtools/目录及其资源的包)的path依赖。完成此操作后,在该包上运行pub get。* 如果你的扩展需要运行中的应用程序,那么你需要运行依赖于你的扩展的应用程序。 * 如果你的扩展不需要运行中的应用程序,那么你需要在支持的 IDE(VS Code 或 IntelliJ / Android Studio)中打开依赖于你的包的测试 Dart 或 Flutter 项目。
- 启动 DevTools
使用以下任一方式启动 DevTools:
* 如果你的扩展需要运行中的应用程序,你可以通过运行测试应用时打印到命令行的 URI,或者从运行测试应用的 IDE 中打开 DevTools。 * 如果你的扩展不需要运行中的应用程序,你可以在支持的 IDE(VS Code 或 IntelliJ / Android Studio)中打开依赖于你的包的 Dart 或 Flutter 项目。从 IDE 打开 DevTools 以在浏览器中查看你的扩展。 * 如果你需要来自 DevTools 的本地或未发布的更改,则需要从源代码构建并运行 DevTools。有关操作指南,请参阅 DevTools CONTRIBUTING.md。你需要构建带有服务器和前端的 DevTools 才能测试扩展(说明)。
- 如果你的测试应用尚未连接到 DevTools,请将其连接,你应该会在 DevTools 应用栏中看到你的扩展标签页。扩展的启用或禁用状态由 DevTools 管理,可通过屏幕右上角操作按钮中的 **Extensions** 菜单进行设置。
打开 DevTools 后,DevTools 应用栏中应该会出现一个扩展标签页。扩展的启用或禁用状态由 DevTools 管理,可通过屏幕右上角操作按钮中提供的“Extensions”菜单进行访问。
发布带有 DevTools 扩展的包
#为了让包向用户提供 DevTools 扩展,必须在发布时在 your_pub_package/extension/devtools/ 目录中包含预期的内容(如前面的设置说明中所述)。
- 确保
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 - 使用
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 扩展提供的工具的使用权限。
你可能会觉得以下链接很有用:
- 有关此功能的最新信息,请访问 GitHub 上的 DevTools Extensions README。
- 如有问题和功能需求,请在 DevTools 问题追踪器上 提交 issue。
- 如需常规支持并加入 DevTools 扩展作者社区,请查看 #devtools-extension-authors Discord 频道(你需要先加入 Flutter Discord 服务器)。