graphview_plus 0.1.1 copy "graphview_plus: ^0.1.1" to clipboard
graphview_plus: ^0.1.1 copied to clipboard

Interactive force-directed graph for Flutter: d3-style physics that settles without jitter, non-overlapping nodes, pan/zoom, drag, hover and an Obsidian-like reveal animation.

[Frame]

πŸ•ΈοΈ GraphView Plus #

graphview_plus is an interactive force-directed graph widget for Flutter. It lays out nodes with d3-style physics that cools down and stops, keeps circles from overlapping, and lets users pan, zoom, drag nodes and tap them. Depends on nothing but Flutter.

[Graph of tasks laid out by graphview_plus]


πŸ“‹ Features #

  • Settles, then sleeps: forces are scaled by a cooling alpha, so the layout comes to rest in a few seconds and stops repainting β€” no endless jitter, no idle CPU.
  • No overlaps: a collision pass keeps every circle apart; hubs get longer spokes so all their leaves fit.
  • Interactive: pan, pinch/scroll zoom, drag a node (the rest of the graph follows), hover highlight, tap callback.
  • Reveal animation: nodes pop in one by one next to a visible neighbour, Obsidian-style.
  • Controller API: add or remove nodes and links at any time; known nodes keep their place.
  • Tunable: repulsion, link distance, link stiffness and centring are plain properties.
  • Headless core: GraphSimulation is pure Dart β€” unit-test layouts or drive your own renderer.

πŸš€ Installation #

Add the dependency in your pubspec.yaml:

dependencies:
  graphview_plus: ^latest_version

Then run:

flutter pub get

πŸ“– Usage #

1. Create a controller #

The type parameter is whatever you want to carry on each node β€” a model, an id, anything:

import 'package:graphview_plus/graphview_plus.dart';

final controller = GraphController<Task>();
controller.setGraph(
  nodes: [
    for (final task in tasks)
      GraphNode(
        id: task.id,          // how nodes are recognised across updates
        label: task.title,    // initials on the circle, full text as a label
        color: task.color,
        data: task,
      ),
  ],
  links: [
    for (final relation in relations)
      GraphLink(relation.parentId, relation.childId),
  ],
);

Call setGraph again whenever your data changes: nodes with a known id keep their position, new ones animate in, removed ones disappear. For single edits there are addNode, removeNode, addLink, removeLink and clear.

3. Show it #

GraphView<Task>(
  controller: controller,
  showLabels: true,
  onNodeTap: (node) => openTask(node.data!),
)

4. Tune the layout #

controller
  ..repulsion = 1.2        // 0.1–20, how hard nodes push apart (default 0.8)
  ..linkDistance = 200     // rest length of a link in canvas px (default 150)
  ..linkStrength = 0.15    // 0–0.3 spring stiffness
  ..centerForce = 0.0003   // 0–0.005 gentle pull towards the centre
  ..physicsEnabled = false; // freeze the layout; drag and reveal keep working

Changing a parameter warms the simulation up again so the layout adapts.

5. Recolour without disturbing the layout #

GraphNode.color is mutable. Change it and ask for a repaint:

for (final task in tasks) {
  controller.nodeById(task.id)?.color = colorFor(task);
}
controller.refresh();

Edge and highlight colours default to the theme's onSurface and primary; override them with GraphView.edgeColor and GraphView.highlightColor.

Full example #

import 'package:flutter/material.dart';
import 'package:graphview_plus/graphview_plus.dart';

void main() => runApp(const ExampleApp());

class ExampleApp extends StatefulWidget {
  const ExampleApp({super.key});

  @override
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  final _controller = GraphController<String>();

  @override
  void initState() {
    super.initState();
    _controller.setGraph(
      nodes: [
        for (var i = 0; i < 30; i++)
          GraphNode(id: '$i', label: 'Node $i', data: 'Node $i'),
      ],
      links: [
        for (var i = 1; i < 10; i++) GraphLink('0', '$i'),
        for (var i = 10; i < 29; i++) GraphLink('$i', '${i + 1}'),
      ],
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => MaterialApp(
    home: Scaffold(
      body: GraphView<String>(
        controller: _controller,
        showLabels: true,
        onNodeTap: (node) => debugPrint(node.data),
      ),
    ),
  );
}

βš™οΈ How the layout works #

The simulation follows d3-force:

  • forces are multiplied by an alpha that decays to zero over ~300 frames;
  • velocities keep 60 % of their value each frame;
  • many-body repulsion falls off as 1/d with a horizon of 2.5 link lengths, so distant nodes do not blow a hub's leaves into a tight fan;
  • links are springs whose rest length grows with a hub's degree, so leaves fit around it;
  • the centre of mass is kept at the origin by translation, which never squeezes the graph;
  • a dragged node is pinned and keeps the simulation warm until it is released.

πŸ“š API Reference #

  • GraphController<T> β€” owns nodes, links, physics and the reveal queue (ChangeNotifier)

    • setGraph({nodes, links}) β€” replace the graph; known ids keep their place
    • addNode(node), removeNode(id), addLink(link), removeLink(link), clear()
    • nodes, edges, nodeById(id), hitTest(canvasPoint)
    • repulsion, linkDistance, linkStrength, centerForce, physicsEnabled
    • refresh() β€” repaint after a colour change; reheat([alpha]) β€” warm the layout up
    • step() β€” advance one frame; GraphView calls it for you
  • GraphView<T> β€” the widget

    • controller, onNodeTap(node), showLabels
    • initialScale, minScale, maxScale
    • highlightColor, edgeColor
  • GraphNode<T> β€” id, label, color, size, data, position

  • GraphLink β€” sourceId, targetId

  • GraphSimulation<T> β€” the headless physics: tick(), reheat(), isSettled, the four tuning fields

2
likes
0
points
379
downloads

Publisher

verified publisherbangertstudio.kz

Weekly Downloads

Interactive force-directed graph for Flutter: d3-style physics that settles without jitter, non-overlapping nodes, pan/zoom, drag, hover and an Obsidian-like reveal animation.

Repository (GitHub)
View/report issues

Topics

#graph #force-directed #visualization #network #canvas

License

unknown (license)

Dependencies

flutter

More

Packages that depend on graphview_plus