跳转至

Dart VM 扩展开发

适用于支持 dartVmDevTool 扩展点的 FlutterX 版本

FlutterX 的 Dart VM ToolWindow 通过 shop.itbug.FlutterCheckVersionX.dartVmDevTool 扩展点开放 tab 能力。内置的 Vm、Memory、Http Monitor、Logging、Provider、Shared Preferences、Hive CE、Drift DB 都使用同一个扩展点注册,第三方插件也可以用同样的方式加入自己的 Dart VM DevTools。

接入方式

第三方插件需要先依赖 FlutterX,然后在 FlutterX 的命名空间下注册 dartVmDevTool

<depends>shop.itbug.FlutterCheckVersionX</depends>

<extensions defaultExtensionNs="shop.itbug.FlutterCheckVersionX">
    <dartVmDevTool
        id="myTool"
        implementation="com.example.MyDartVmDevTool"
        descriptionKey="my.tool.description"
        order="after hive"/>
</extensions>

扩展属性

属性 是否必填 说明
id 稳定的 tab ID。FlutterX 会用它保存用户隐藏 tab 的设置,发布后不建议修改。
implementation 实现 shop.itbug.flutterx.api.vm.DartVmDevToolExtension 的类。
descriptionKey 多语言描述 key。FlutterX 会从贡献插件的 resource bundle 读取文本,并显示在 Dart VM 设置页里。
order IntelliJ 扩展排序规则,例如 firstlastafter hivebefore drift

如果没有配置 descriptionKey,或者 key 在 resource bundle 中不存在,设置页不会显示描述文本。

实现扩展类

扩展类建议保持无状态,不要在构造函数里执行耗时逻辑。真正的 UI 和 VM Service 访问应放到 createComponent 返回的组件生命周期里处理。

package com.example

import com.intellij.openapi.project.Project
import shop.itbug.flutterx.api.vm.DartVmDevToolContext
import shop.itbug.flutterx.api.vm.DartVmDevToolExtension
import java.awt.BorderLayout
import javax.swing.JComponent
import javax.swing.JLabel
import javax.swing.JPanel

class MyDartVmDevTool : DartVmDevToolExtension {
    override fun getTabTitle(project: Project): String = "My Tool"

    override fun isAvailable(context: DartVmDevToolContext): Boolean {
        return true
    }

    override fun createComponent(context: DartVmDevToolContext): JComponent {
        return JPanel(BorderLayout()).apply {
            add(JLabel("My FlutterX Dart VM tool"), BorderLayout.CENTER)
        }
    }
}

createComponent 返回的是 Swing JComponent。FlutterX 不把 Compose 或 Jewel 类型暴露给第三方插件,这样可以减少 Kotlin、Compose、Jewel 版本兼容问题。

读取运行中的 Flutter 应用

DartVmDevToolContext 提供当前项目、ToolWindow 和运行中的 Flutter 应用状态。

interface DartVmDevToolContext {
    val project: Project
    val toolWindow: ToolWindow
    val runningApps: StateFlow<List<DartVmApp>>
}

runningApps 是动态状态。如果用户先打开 FlutterX Dart VM ToolWindow,再启动 Flutter 应用,扩展组件需要订阅 context.runningApps 才能收到更新。

interface DartVmApp {
    val appId: String
    val vmUrl: String
    val deviceId: String
    val mode: String
    val vmService: VmService
}

可以通过 DartVmApp.vmService 调用 Dart VM Service RPC。VM 调用可能耗时或失败,不要阻塞 EDT。

多语言描述

如果想在 Dart VM 设置页展示 tab 描述,可以配置 descriptionKey,并在第三方插件自己的 resource bundle 里提供文本。

<idea-plugin>
    <resource-bundle>messages.MyPluginBundle</resource-bundle>

    <depends>shop.itbug.FlutterCheckVersionX</depends>

    <extensions defaultExtensionNs="shop.itbug.FlutterCheckVersionX">
        <dartVmDevTool
            id="myTool"
            implementation="com.example.MyDartVmDevTool"
            descriptionKey="my.tool.description"
            order="after hive"/>
    </extensions>
</idea-plugin>
my.tool.description=Shows custom diagnostics collected from Dart VM Service.

用户显示和隐藏 tab

FlutterX 会在 Dart VM 设置页自动列出所有已注册的 Dart VM tab,包括第三方插件贡献的 tab。tab 默认显示,用户取消勾选后不会再显示。

隐藏设置通过扩展的 id 保存。已经发布的第三方插件不要随意修改 id,否则用户之前的显示或隐藏偏好会失效。

注意事项

  • v1 不支持第三方插件安装或卸载后即时刷新已经打开的 Dart VM tab,通常需要重新打开 ToolWindow 或重启 IDE。
  • 某个扩展创建组件失败时,FlutterX 会跳过该 tab,并继续加载其他 tab。
  • 使用 isAvailable 可以按项目、依赖或运行状态决定是否显示 tab。
  • 组件需要自己处理释放逻辑,避免后台协程、监听器或 VM Service 订阅泄漏。

验证

开发第三方扩展时建议验证以下场景:

  • ./gradlew compileKotlin 能通过。
  • ./gradlew verifyPluginStructure 能通过。
  • 打开 FlutterX Dart VM 后能看到第三方 tab。
  • order="after hive" 等排序规则生效。
  • 先打开 ToolWindow 再运行 Flutter 应用,订阅 runningApps 的 UI 能正常刷新。
  • 在 Dart VM 设置页取消勾选后,tab 不再显示;重新勾选后恢复显示。