graphview_plus 0.1.2 copy "graphview_plus: ^0.1.2" to clipboard
graphview_plus: ^0.1.2 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
160
points
379
downloads

Documentation

API reference

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

MIT (license)

Dependencies

flutter

More

Packages that depend on graphview_plus