Flutter 终于有自己的 React Flow 了,vyuh_node_flow 上手实录

话说去年搞鸿蒙开发者激励时,我曾想到搞一个Xmind之类的APP。说实话,与其相信我自己可以写一个这么复杂的系统,还不如相信自己是秦始皇。这种节点图、连线、缩放平移的东西,交互细节特别多,自己从零开始写,文件夹刚建就得烂尾。由于当时我的那几个app全是flutter做的。所以我就尝试找一个可视化的流程编辑器库,让用户能自己拖拽节点、连线、拼出一个工作流。没想到还真让找到了:vyuh_node_flow。


01 它到底是什么

简单地说:Flutter 里的 React Flow。

前端同学应该都知道 React Flow,React 生态里做节点图编辑器的事实标准,GitHub 上两万多 star。而 vyuh_node_flow 的作者也很坦诚,在 README 里直接写明这个项目就是 inspired by React Flow。

但它是纯 Dart 写的,原生 Flutter Widget,不依赖任何 JS 引擎或 WebView。这意味着它跑在 iOS、Android、桌面、Web 上都是一套代码,没有跨端的心智负担。

那它能拿来干什么?我总结了四类典型场景:

  • 可视化编程界面------像 Scratch 或者 Unreal Engine 蓝图那种,拖节点、连线、拼逻辑。
  • 工作流编辑器------业务流程、数据处理、自动化任务的可视化编排。
  • 交互式图表------组织架构图、思维导图、状态机。
  • 数据管道------把数据流向和处理步骤画出来,看得见摸得着。

说白了,凡是「节点 + 连线」能表达的图,它都能接。

官方 Demo 里的实际交互效果


02 上手有多快

我试了下,从零跑通一个能拖拽、能连线的流程图,大概三十行代码。

核心就三步:建一个 Controller、塞几个 Node 进去、丢给 NodeFlowEditor 渲染。

dart 复制代码
import 'package:flutter/material.dart';
import 'package:vyuh_node_flow/vyuh_node_flow.dart';

class FlowEditor extends StatefulWidget {
  @override
  State<FlowEditor> createState() => _FlowEditorState();
}

class _FlowEditorState extends State<FlowEditor> {
  // 1. 建一个 Controller,泛型是你的节点数据类型
  late final controller = NodeFlowController<String, dynamic>(
    nodes: [
      Node<String>(
        id: 'node-1',
        type: 'input',
        position: const Offset(100, 100),
        data: '开始',
        outputPorts: const [
          Port(id: 'out', name: 'Output', offset: Offset(2, 40)),
        ],
      ),
      Node<String>(
        id: 'node-2',
        type: 'output',
        position: const Offset(400, 100),
        data: '结束',
        inputPorts: const [
          Port(id: 'in', name: 'Input', offset: Offset(-2, 40)),
        ],
      ),
    ],
    // 2. 把两个节点连起来
    connections: [
      Connection(
        id: 'conn-1',
        sourceNodeId: 'node-1',
        sourcePortId: 'out',
        targetNodeId: 'node-2',
        targetPortId: 'in',
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    // 3. 丢给编辑器,配一个 nodeBuilder 决定节点长啥样
    return NodeFlowEditor<String, dynamic>(
      controller: controller,
      theme: NodeFlowTheme.light,
      nodeBuilder: (context, node) => Padding(
        padding: const EdgeInsets.all(16),
        child: Text(node.data,
            style: const TextStyle(fontWeight: FontWeight.bold)),
      ),
    );
  }
}

跑起来就是上面那张 GIF 的样子:两个节点、一条连线,能拖、能选、能缩放。

这里有个小细节我觉得挺关键------它用了泛型。NodeFlowController<String, dynamic> 里的 String 就是你的节点数据类型,nodeBuilder 回调里拿到的 node.data 就是强类型的 String,不用 cast、不用 as。作者也建议用 sealed class 做节点数据层级,配合 switch 表达式做模式匹配,类型安全能拉满。

这对 Flutter 开发者来说很对味,比那些靠 Map<String, dynamic> 传数据的库舒服太多了。


03 设计上有点东西

光看 API 用法不够过瘾,我去翻了翻源码,发现这个库的内部设计其实有点东西,不是那种「能跑就行」的玩具项目。

状态管理用了 MobX

项目里大量用了 Observable、Computed、action、Observer 这套 MobX 模式。节点位置、选中状态这些是 Observable,一变,对应的 Observer Widget 自动重绘,不用你手动 setState。好处是局部刷新很精准,不会因为一个节点动了,整张图重绘一遍。

分层渲染

编辑器内部用 Stack 把不同元素分到不同层:网格层、背景注解层、连接线层、连接标签层、节点层、前景注解层、交互层。连接标签单独一层是有讲究的------连接线重绘时,标签不用跟着重绘。再配合 RepaintBoundary 隔离每层,把重绘范围压到最小。

性能上做了几层缓存

连接线的 Path 算出来之后会缓存(ConnectionPathCache),命中测试也复用这条缓存,节点不动就不重算。还有一个基于网格的空间索引(SpatialIndex),查「这块区域有哪些节点」不用遍历全量,几百个节点时命中测试仍然快。官方说 100+ 节点能稳 60fps,我手头没铺到那个量级,但从实现策略看,不是空口说白话。

平移和缩放主要靠 Flutter 自带的 InteractiveViewer,没自己重新发明轮子,这点我也挺认同的------能用框架能力就用,把精力留给真正难的连接和命中逻辑。


04 几个我觉得亮眼的细节

功能清单谁都能抄,真正区分一个库好坏的是细节。说几个我比较喜欢的。

连接线能做动效

不只是画一条线那么简单。内置了粒子流(Particles)、流动虚线(FlowingDash)、脉冲(Pulse)、渐变(Gradient)、彩虹(Rainbow)几种特效。做数据管道可视化的时候,在线上加个流动虚线,数据「在流」的感觉一下就出来了,比静态箭头生动太多。

主题系统粒度很细

NodeFlowTheme 拆成了 NodeTheme、ConnectionTheme、PortTheme、GridTheme 几个子主题,每个都能 copyWith。深色浅色开箱即用,但要改端口形状、连接线颜色、网格样式,都有一层一层可调。做 toB 产品的人应该会喜欢这种可控度。

dart 复制代码
final customTheme = NodeFlowTheme.dark.copyWith(
  connectionTheme: ConnectionTheme.dark.copyWith(
    style: ConnectionStyles.bezier,
    color: Colors.purple.shade300,
    animationEffect: ConnectionEffects.particles,
  ),
  gridTheme: GridTheme.dark.copyWith(
    style: GridStyles.dots,
    color: Colors.white24,
  ),
  nodeTheme: NodeTheme.dark.copyWith(
    borderRadius: BorderRadius.circular(12),
    selectedBorderColor: Colors.purple,
  ),
);

注解系统能跟着节点走

画布上能贴便签(StickyAnnotation)、打标记(MarkerAnnotation)、框分组(GroupAnnotation)。分组框会自动包围它依赖的节点,节点一动,框跟着伸缩。这在复杂流程图里特别有用------几百个节点没有分组注释,根本没法看。

整图能序列化成 JSON

节点、连接、注解、视口状态,全套 toJson / fromJson。用户拼好的流程图存到后端,下次打开原样恢复,不用自己写序列化。做 toB 工作流产品,这个能力是刚需。

还有几个锦上添花的:迷你地图(Minimap)做大局导航、20 多个内置快捷键、只读模式 NodeFlowViewer 给纯展示场景用、插件式扩展(StatsExtension、DebugExtension、LodExtension)按需挂载。


05 说说不足

夸了这么多,也得说说它现在还缺什么,免得你上手了才发现坑。

最大的缺口是没有自动布局算法。节点位置得你自己给 Offset,或者用户手动拖。如果你有一堆数据想自动生成一张整齐的流程图,vyuh_node_flow 帮不了你,得自己接 dagre、graphviz 这类布局引擎算坐标,再喂给它的 Controller。

另外它还比较年轻,版本号还在 0.x 阶段,API 可能有变动。做正式产品接入前,建议先把 changelog 扫一遍,心里有数。

但话说回来,在 Flutter 生态里,节点流程编辑器这个品类,目前能打的也就它了。React Flow 在前端能长成两万 star 的基础设施,靠的不是一天写完,是几年迭代。vyuh_node_flow 的底子已经铺得不差,缺的是时间和用户反馈。


06 说点更远的

用完 vyuh_node_flow,我更想说的一件事是------Flutter 生态在可视化编辑这块,正在补课。

前端有 React Flow、有 drawflow、有 G6、有 X6,做节点图、关系图、流程图,工具链已经成熟到「npm install 就能跑」。Flutter 这边过去是真空地带,要做类似的东西,要么套 WebView 引一个 JS 库,要么自己啃 CustomPaint 从头造。前者跨端体验割裂,后者成本高得吓人。

vyuh_node_flow 这种纯 Dart 原生方案出现的意义,不只是多一个轮子。它意味着 Flutter 想做 toB 的可视化编辑器、做低代码平台、做工作流编排工具,终于不用在「体验妥协」和「成本爆炸」之间二选一了。

而且它的设计是贴着 Flutter 原生习惯去的------泛型、Widget builder、Theme copyWith、Observable 局部刷新,全是 Flutter 开发者肌肉记忆里的那一套。没有引入一套新的心智模型,学习曲线很平。

如果你最近正好要在 Flutter 里画流程图、做节点编辑、搭可视化编排工具,不妨先拿它试试,别急着造轮子。

项目地址放在下面了,文档站也有在线 Demo 可以直接上手体验。

相关推荐
花开盛夏^.^5 小时前
电脑打开多个浏览器Web窗口占用较大的运行内存的原因
前端·浏览器
馒头不想说话6 小时前
实体店老板的24小时AI运营员工,写、剪、发全自动,省下一整个团队工资
前端
椒哥8 小时前
一天被问几十次"允许",我统计了 517 次,然后写了个插件
前端·javascript·github
风骏时光牛马8 小时前
生成一个AI稳定性治理及疑难问题分析标题,只需要一个标题,不需要其他的任何文本。
前端
莫西干式秃头8 小时前
用 AI + threejs 写了个浏览器里的 3D 动作游戏《武来》:砍怪、放技能、开 SU7
前端·人工智能·three.js
kyriewen9 小时前
我测了 6 种防请求竞态的写法:只有 1 种真能打
前端·javascript·程序员
孟健9 小时前
用 WorkBuddy 做个人名片网页,挂上开源设计技能后效果完全不同了
前端·人工智能·ai编程
IT_陈寒9 小时前
SpringBoot自动配置的坑,这次终于爬出来了
前端·人工智能·后端
LuHang10 小时前
从天地图切换到高德地图:一次真实的底图迁移实战
前端·vue.js·cesium
用户3600555790010 小时前
Kestrel_VLLM - 27 · 寄存器常驻与融合发射:两次「少发一次」
前端