11
11
<a title =" Pub " href =" https://flame-engine.org " >
12
12
<img src="https://img.shields.io/badge/Powered%20by-%F0%9F%94%A5-orange.svg" />
13
13
</a >
14
- <a title =" Powered by Flame " href =" https://pub.flutter-io.cn /packages/flutter_graph_view " >
15
- <img src="https://img.shields.io/badge/Pub-v0.0.1+ x-red?style=popout" />
14
+ <a title =" Powered by Flame " href =" https://pub.dev /packages/flutter_graph_view " >
15
+ <img src="https://img.shields.io/badge/Pub-v1. x-red?style=popout" />
16
16
</a >
17
17
<a href =" https://github.com/dudu-ltd/flutter_graph_view/stargazers " >
18
18
<img src="https://img.shields.io/github/stars/dudu-ltd/flutter_graph_view" alt="GitHub stars" />
24
24
25
25
致力于图数据的可视化组件
26
26
27
- ![ demo ] ( https://foruda.gitee.com/images/1674684822685415888/5033481e_1043207 .png )
27
+ ![ 输入图片说明 ] ( https://foruda.gitee.com/images/1712005397846215598/d97f4d7e_1043207 .png " 屏幕截图 " )
28
28
29
- ![ 多边节点] ( https://foruda.gitee.com/images/1675837598136195029/03b27259_1043207.png " 屏幕截图 ")
29
+ ![ 输入图片说明] ( https://foruda.gitee.com/images/1712005408211711810/bc5a6037_1043207.png " 屏幕截图 ")
30
+
31
+ ![ 输入图片说明] ( https://foruda.gitee.com/images/1712005417532504522/1d44cdfe_1043207.png " 屏幕截图 ")
30
32
31
33
## 特性
32
34
36
38
- [x] 力导向图法,雏形已实现
37
39
- [x] 支持定位算法装饰器
38
40
- [x] 提供呼吸效果的自定义装饰器(可选特性)
41
+ - [x] 为相关连节点提供遵循胡克定律的装饰器
42
+ - [x] 为所有节点提供由中心向外的胡克定律的装饰器
43
+ - [x] 为子图根节点提供互斥的的库伦定律装饰器
44
+ - [x] 为所有节点提供边缘缓冲碰撞的胡克定律装饰器
45
+ - [x] 在图中追加一个计数器装饰器,用于将节点受力转换成运动
39
46
- [x] 提供数据面板的嵌入
40
47
- [x] 提供样式配置
41
48
- [ ] 提供更多交互能力
@@ -58,122 +65,137 @@ import 'dart:math';
58
65
import 'package:flutter/material.dart';
59
66
import 'package:flutter_graph_view/flutter_graph_view.dart';
60
67
61
- void main() {
62
- var vertexes = <Map>{};
63
- var r = Random();
64
- for (var i = 0; i < 200; i++) {
65
- vertexes.add(
66
- {
67
- 'id': 'node$i',
68
- 'tag': 'tag${r.nextInt(9)}',
69
- 'tags': [
70
- 'tag${r.nextInt(9)}',
71
- if (r.nextBool()) 'tag${r.nextInt(4)}',
72
- if (r.nextBool()) 'tag${r.nextInt(8)}'
68
+ class DecoratorDemo extends StatelessWidget {
69
+ const DecoratorDemo({super.key});
70
+
71
+ @override
72
+ Widget build(BuildContext context) {
73
+ var vertexes = <Map>{};
74
+ var r = Random();
75
+ for (var i = 0; i < 150; i++) {
76
+ vertexes.add(
77
+ {
78
+ 'id': 'node$i',
79
+ 'tag': 'tag${r.nextInt(9)}',
80
+ 'tags': [
81
+ 'tag${r.nextInt(9)}',
82
+ if (r.nextBool()) 'tag${r.nextInt(4)}',
83
+ if (r.nextBool()) 'tag${r.nextInt(8)}'
84
+ ],
85
+ },
86
+ );
87
+ }
88
+ var edges = <Map>{};
89
+
90
+ for (var i = 0; i < 150; i++) {
91
+ edges.add({
92
+ 'srcId': 'node${i % 8 + 8}',
93
+ 'dstId': 'node$i',
94
+ 'edgeName': 'edge${r.nextInt(3)}',
95
+ 'ranking': DateTime.now().millisecond,
96
+ });
97
+ }
98
+ // for (var i = 0; i < 20; i++) {
99
+ // edges.add({
100
+ // 'srcId': 'node${i % 8}',
101
+ // 'dstId': 'node${r.nextInt(150)}',
102
+ // 'edgeName': 'edge${r.nextInt(3)}',
103
+ // 'ranking': DateTime.now().millisecond,
104
+ // });
105
+ // }
106
+
107
+ var data = {
108
+ 'vertexes': vertexes,
109
+ 'edges': edges,
110
+ };
111
+ return FlutterGraphWidget(
112
+ data: data,
113
+ algorithm: RandomAlgorithm(
114
+ decorators: [
115
+ CoulombDecorator(),
116
+ // HookeBorderDecorator(),
117
+ HookeDecorator(),
118
+ CoulombCenterDecorator(),
119
+ HookeCenterDecorator(),
120
+ ForceDecorator(),
121
+ ForceMotionDecorator(),
122
+ TimeCounterDecorator(),
73
123
],
74
- },
124
+ ),
125
+ convertor: MapConvertor(),
126
+ options: Options()
127
+ ..enableHit = false
128
+ ..panelDelay = const Duration(milliseconds: 500)
129
+ ..graphStyle = (GraphStyle()
130
+ // tagColor is prior to tagColorByIndex. use vertex.tags to get color
131
+ ..tagColor = {'tag8': Colors.orangeAccent.shade200}
132
+ ..tagColorByIndex = [
133
+ Colors.red.shade200,
134
+ Colors.orange.shade200,
135
+ Colors.yellow.shade200,
136
+ Colors.green.shade200,
137
+ Colors.blue.shade200,
138
+ Colors.blueAccent.shade200,
139
+ Colors.purple.shade200,
140
+ Colors.pink.shade200,
141
+ Colors.blueGrey.shade200,
142
+ Colors.deepOrange.shade200,
143
+ ])
144
+ ..useLegend = true // default true
145
+ ..edgePanelBuilder = edgePanelBuilder
146
+ ..vertexPanelBuilder = vertexPanelBuilder
147
+ ..edgeShape = EdgeLineShape() // default is EdgeLineShape.
148
+ ..vertexShape = VertexCircleShape(), // default is VertexCircleShape.
75
149
);
76
150
}
77
- var edges = <Map>{};
78
-
79
- for (var i = 0; i < 200; i++) {
80
- edges.add({
81
- 'srcId': 'node${i % 4}',
82
- 'dstId': 'node$i',
83
- 'edgeName': 'edge${r.nextInt(3)}',
84
- 'ranking': r.nextInt(DateTime.now().millisecond),
85
- });
86
- }
87
-
88
- for (var i = 0; i < 50; i++) {
89
- edges.add({
90
- 'srcId': 'node1',
91
- 'dstId': 'node2',
92
- 'edgeName': 'edge${r.nextInt(3)}',
93
- 'ranking': r.nextInt(DateTime.now().millisecond),
94
- });
95
- }
96
-
97
- var data = {
98
- 'vertexes': vertexes,
99
- 'edges': edges,
100
- };
101
-
102
- runApp(MaterialApp(
103
- home: Scaffold(
104
- body: FlutterGraphWidget(
105
- data: data,
106
- algorithm: ForceDirected(BreatheDecorator()),
107
- convertor: MapConvertor(),
108
- options: Options()
109
- ..graphStyle = (GraphStyle()
110
- // tagColor is prior to tagColorByIndex. use vertex.tags to get color
111
- ..tagColor = {'tag8': Colors.orangeAccent.shade200}
112
- ..tagColorByIndex = [
113
- Colors.red.shade200,
114
- Colors.orange.shade200,
115
- Colors.yellow.shade200,
116
- Colors.green.shade200,
117
- Colors.blue.shade200,
118
- Colors.blueAccent.shade200,
119
- Colors.purple.shade200,
120
- Colors.pink.shade200,
121
- Colors.blueGrey.shade200,
122
- Colors.deepOrange.shade200,
123
- ])
124
- ..useLegend = true // default true
125
- ..edgePanelBuilder = edgePanelBuilder
126
- ..vertexPanelBuilder = vertexPanelBuilder
127
- ..edgeShape = EdgeLineShape() // default is EdgeLineShape.
128
- ..vertexShape = VertexCircleShape(), // default is VertexCircleShape.
129
- ),
130
- ),
131
- ));
132
- }
133
151
134
- Widget edgePanelBuilder(Edge edge) {
135
- var c = (edge.start.cpn!.position + edge.end!.cpn!.position) / 2;
136
- return Stack(
137
- children: [
138
- Positioned(
139
- left: c.x + 5,
140
- top: c.y,
141
- child: SizedBox(
142
- width: 150,
143
- child: ColoredBox(
144
- color: Colors.white,
145
- child: ListTile(
146
- title: Text('${edge.edgeName} @${edge.ranking}'),
152
+ Widget edgePanelBuilder(Edge edge, Viewfinder viewfinder) {
153
+ var c = viewfinder.localToGlobal(edge.position);
154
+
155
+ return Stack(
156
+ children: [
157
+ Positioned(
158
+ left: c.x + 5,
159
+ top: c.y,
160
+ child: SizedBox(
161
+ width: 200,
162
+ child: ColoredBox(
163
+ color: Colors.grey.shade900.withAlpha(200),
164
+ child: ListTile(
165
+ title: Text(
166
+ '${edge.edgeName} @${edge.ranking}\nDelay controlled by \noptions.panelDelay\ndefault to 300ms'),
167
+ ),
147
168
),
148
169
),
149
- ),
150
- )
151
- ],
152
- );
153
- }
170
+ )
171
+ ],
172
+ );
173
+ }
154
174
155
- Widget vertexPanelBuilder(hoverVertex) {
156
- return Stack(
157
- children: [
158
- Positioned(
159
- left: hoverVertex.cpn!.position.x + hoverVertex.radius + 5,
160
- top: hoverVertex.cpn!.position.y - 20,
161
- child: SizedBox(
162
- width: 120,
163
- child: ColoredBox(
164
- color: Colors.white,
165
- child: ListTile(
166
- title: Text(
167
- 'Id: ${hoverVertex.id}',
175
+ Widget vertexPanelBuilder(hoverVertex, Viewfinder viewfinder) {
176
+ var c = viewfinder.localToGlobal(hoverVertex.cpn!.position);
177
+ return Stack(
178
+ children: [
179
+ Positioned(
180
+ left: c.x + hoverVertex.radius + 5,
181
+ top: c.y - 20,
182
+ child: SizedBox(
183
+ width: 120,
184
+ child: ColoredBox(
185
+ color: Colors.grey.shade900.withAlpha(200),
186
+ child: ListTile(
187
+ title: Text(
188
+ 'Id: ${hoverVertex.id}',
189
+ ),
190
+ subtitle: Text(
191
+ 'Tag: ${hoverVertex.data['tag']}\nDegree: ${hoverVertex.degree} ${hoverVertex.prevVertex?.id}'),
168
192
),
169
- subtitle: Text(
170
- 'Tag: ${hoverVertex.data['tag']}\nDegree: ${hoverVertex.degree}'),
171
193
),
172
194
),
173
- ),
174
- )
175
- ],
176
- );
195
+ )
196
+ ],
197
+ );
198
+ }
177
199
}
178
200
179
201
```
0 commit comments