graphview_plus 0.1.0
graphview_plus: ^0.1.0 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.
example/lib/main.dart
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();
// A hub with leaves and a chain: the shapes that stress a layout.
const palette = [Color(0xFF6C8EF5), Color(0xFF55C7A5), Color(0xFFF2A65A)];
_controller.setGraph(
nodes: [
for (var i = 0; i < 30; i++)
GraphNode(
id: '$i',
label: 'Node $i',
color: palette[i % palette.length],
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(
theme: ThemeData.dark(),
home: Scaffold(
appBar: AppBar(
title: const Text('graphview_plus'),
actions: [
ListenableBuilder(
listenable: _controller,
builder: (context, _) => IconButton(
icon: Icon(
_controller.physicsEnabled ? Icons.pause : Icons.play_arrow,
),
onPressed: () =>
_controller.physicsEnabled = !_controller.physicsEnabled,
),
),
],
),
body: GraphView<String>(
controller: _controller,
showLabels: true,
onNodeTap: (node) => ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(node.data!))),
),
),
);
}