GraphView

Flutter GraphView 用于在图结构中显示数据。它可以显示树状布局和有向图。

该库旨在支持不同的图布局,目前对小型图效果极佳。

您可以在此处查看 Flutter Web 实现
http://graphview.surge.sh/

graphvi

grapshvi

布局

使用 Walker 算法并结合 Buchheim 的运行时改进(BuchheimWalkerAlgorithm 类)。支持不同的方向。您只需使用 BuchheimWalkerConfiguration.orientation 并选择 ORIENTATION_LEFT_RIGHTORIENTATION_RIGHT_LEFTORIENTATION_TOP_BOTTOM
ORIENTATION_BOTTOM_TOP(默认)。此外,还可以设置兄弟节点、层级、子树等分隔参数。

适用于:家族树、层级视图、Flutter Widget 树,

有向图

通过模拟吸引/排斥力绘制有向图。为此,实现了 Fruchterman 和 Reingold 的算法(FruchtermanReingoldAlgorithm 类)。

适用于:社交网络、思维导图、集群、图、城市间道路网络,

用法

目前 GraphView 必须与缩放引擎一起使用,例如 InteractiveViewer。要更改缩放值,只需使用 InteractiveViewer 类中描述的不同属性。

要创建图,我们需要实例化 Graph 类。然后我们需要传递布局,还可以选择性地传递边渲染器。

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) => MaterialApp(
        home: GraphViewPage(),
      );
}


class GraphViewPage extends StatefulWidget {
  @override
  _GraphViewPageState createState() => _GraphViewPageState();
}

class _GraphViewPageState extends State<GraphViewPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: Column(
          mainAxisSize: MainAxisSize.max,
          children: [
            Wrap(
              children: [
                Container(
                  width: 100,
                  child: TextFormField(
                    initialValue: builder.siblingSeparation.toString(),
                    decoration: InputDecoration(labelText: "Sibling Sepration"),
                    onChanged: (text) {
                      builder.siblingSeparation = int.tryParse(text) ?? 100;
                      this.setState(() {});
                    },
                  ),
                ),
                Container(
                  width: 100,
                  child: TextFormField(
                    initialValue: builder.levelSeparation.toString(),
                    decoration: InputDecoration(labelText: "Level Seperation"),
                    onChanged: (text) {
                      builder.levelSeparation = int.tryParse(text) ?? 100;
                      this.setState(() {});
                    },
                  ),
                ),
                Container(
                  width: 100,
                  child: TextFormField(
                    initialValue: builder.subtreeSeparation.toString(),
                    decoration: InputDecoration(labelText: "Subtree separation"),
                    onChanged: (text) {
                      builder.subtreeSeparation = int.tryParse(text) ?? 100;
                      this.setState(() {});
                    },
                  ),
                ),
                Container(
                  width: 100,
                  child: TextFormField(
                    initialValue: builder.orientation.toString(),
                    decoration: InputDecoration(labelText: "Orientation"),
                    onChanged: (text) {
                      builder.orientation = int.tryParse(text) ?? 100;
                      this.setState(() {});
                    },
                  ),
                ),
                RaisedButton(
                  onPressed: () {
                    final Node node12 = Node(getNodeText());
                    var edge = graph.getNodeAtPosition(r.nextInt(graph.nodeCount()));
                    print(edge);
                    graph.addEdge(edge, node12);
                    setState(() {});
                  },
                  child: Text("Add"),
                )
              ],
            ),
            Expanded(
              child: InteractiveViewer(
                  constrained: false,
                  scaleEnabled: false,
                  boundaryMargin: EdgeInsets.all(100),
                  minScale: 0.01,
                  maxScale: 5.6,
                  child: GraphView(
                    graph: graph,
                    algorithm: BuchheimWalkerAlgorithm(builder),
                    renderer: TreeEdgeRenderer(builder),
                  )),
            ),
          ],
        )
    );
  }

  Random r = Random();

  int n = 1;

  Widget getNodeText() {
    return Container(
        padding: EdgeInsets.all(16),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(4),
          boxShadow: [
            BoxShadow(color: Colors.blue[100], spreadRadius: 1),
          ],
        ),
        child: Text("Node ${n++}"));
  }

  final Graph graph = Graph();
  BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration();

  @override
  void initState() {
    final Node node1 = Node(getNodeText());
    final Node node2 = Node(getNodeText());
    final Node node3 = Node(getNodeText());
    final Node node4 = Node(getNodeText());
    final Node node5 = Node(getNodeText());
    final Node node6 = Node(getNodeText());
    final Node node8 = Node(getNodeText());
    final Node node7 = Node(getNodeText());
    final Node node9 = Node(getNodeText());
    final Node node10 = Node(getNodeText());
    final Node node11 = Node(getNodeText());
    final Node node12 = Node(getNodeText());

    graph.addEdge(node1, node2);
    graph.addEdge(node1, node3);
    graph.addEdge(node1, node4);
    graph.addEdge(node2, node5);
    graph.addEdge(node2, node6);
    graph.addEdge(node6, node7);
    graph.addEdge(node6, node8);
    graph.addEdge(node4, node9);
    graph.addEdge(node4, node10);
    graph.addEdge(node4, node11);
    graph.addEdge(node11, node12);

    builder
      ..siblingSeparation = (100)
      ..levelSeparation = (150)
      ..subtreeSeparation = (150)
      ..orientation = (BuchheimWalkerConfiguration.ORIENTATION_TOP_BOTTOM);
  }
}

在 Node 中使用任何 Widget

您可以在节点中使用任何 Widget

Node node = Node(getNodeText);

getNodeText() {
    return Container(
        padding: EdgeInsets.all(16),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(4),
          boxShadow: [
            BoxShadow(color: Colors.blue[100], spreadRadius: 1),
          ],
        ),
        child: Text("Node ${n++}"));
  }

示例

根节点树

TopDownTree

根节点树(从下到上)

BottomTopTree

根节点树(从左到右)

LeftRightTree

根节点树(从右到左)

RightLeftTree

有向图

Graph

灵感来源

该库基本上是 Team-Blox 出色的 Android 库 GraphView 的 Dart 版本。

我想感谢他们开源了他们的代码,因此我能够将他们的代码移植到 Dart 并用于 Flutter。

未来工作

[] 添加 nodeOnTap
[] 添加分层图(欢迎 PR)
[] 使用构建器模式按需绘制项目。

GitHub

https://github.com/nabil6391/graphview