Flutter图表展示插件graph_display的使用
Flutter图表展示插件graph_display的使用
这个包提供了可自定义的Flutter图表展示组件。
如果你不使用Flutter或者需要更特定的图表组件,可以考虑使用graph_layout来生成图表布局,并自己编写代码在屏幕上显示这些布局。
示例用法
首先构造你希望可视化的图。
// 创建一个`Graph`对象
// 这个对象是从NetworkX主页上的图创建的 https://networkx.org/
final edgeList = '''
0 1
0 2
0 3
1 2
1 3
2 3
3 4
4 5
5 6
6 7
7 8
7 9
7 10
8 9
8 10
9 10
''';
final graph = Graph.fromEdgeListString(edgeList);
然后,你可以将这个图作为一个小部件展示出来!该图可以是交互式的,允许应用程序的用户拖动节点以探索图;也可以是非交互式的,一旦生成了图布局就不需要再进行计算。
InteractiveGraph(
layoutAlgorithm: Eades(graph: graph),
);
StaticGraph(
layoutAlgorithm: FruchtermanReingold(graph: graph),
)
示例代码
以下是完整的示例代码,展示了如何使用graph_display
插件。
import 'package:example/jean_node_data.dart';
import 'package:flutter/material.dart';
import 'package:graph_display/graph_display.dart';
import 'package:graph_layout/graph_layout.dart';
import 'package:vector_math/vector_math.dart' hide Colors;
const _demoNames = [
'完全图(8个顶点),Eades算法',
'完全图(8个顶点),Fruchterman-Reingold算法',
'《悲惨世界》角色共现,Eades算法',
'《悲惨世界》角色共现,Fruchterman-Reingold算法',
];
/// 返回具有[nodes]个节点的完全图。
Graph _generateCompleteGraph(int nodeNumber) {
final nodes = List<int>.generate(nodeNumber, (i) => i + 1)
.map((i) => IntegerNode(i))
.toList();
final edges = <Edge>{};
for (int i = 0; i < nodeNumber; i++) {
for (int j = 0; j < nodeNumber; j++) {
if (i != j) {
edges.add(Edge(left: nodes[i], right: nodes[j]));
}
}
}
return Graph.fromEdgeList(edges);
}
void main() {
runApp(const ExampleApp());
}
class ExampleApp extends StatefulWidget {
const ExampleApp({Key? key}) : super(key: key);
[@override](/user/override)
State<ExampleApp> createState() => _ExampleAppState();
}
class _ExampleAppState extends State<ExampleApp> {
var _selectedDemoIndex = 0;
Widget _displayDemo() {
switch (_selectedDemoIndex) {
case 0:
return InteractiveGraph(
layoutAlgorithm: Eades(
graph: _generateCompleteGraph(8),
),
);
case 1:
return StaticGraph(
layoutAlgorithm: FruchtermanReingold(
graph: _generateCompleteGraph(8),
),
);
case 2:
case 3:
return FutureBuilder<String>(
future: DefaultAssetBundle.of(context).loadString('assets/jean.dat'),
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
final edges = <Edge>{};
// 每个匹配对应一章,每章包含一个或多个场景。
final matches = RegExp(r'(?<=[0-9]:).*$', multiLine: true)
.allMatches(snapshot.data!)
.map((match) => match[0]!.split(';'))
.expand((e) => e);
// 遍历每个场景。如果两个角色出现在同一个场景中,则认为它们共现。
for (final m in matches) {
// 这个场景中的角色。
final characters = m
.split(',')
.map((characterString) =>
IntegerNode(characterString.hashCode))
.toList();
// 找出这组中的每一对并将其添加为边。无需担心时间复杂度,因为[characters]很小。
for (final char1 in characters) {
for (final char2 in characters) {
if (char1 != char2) {
edges.add(Edge(left: char1, right: char2));
}
}
}
}
final theme = GraphThemePreferences(
backgroundColour: Colors.blueGrey.shade50,
edgeColour: Colors.blueGrey,
edgeThickness: 0.2,
nodeRadius: 15,
drawNode: (Canvas canvas, Node node, Vector2 position) {
// 请参阅jean_node_data.dart以获取此颜色映射的源代码。
final nodePaint = Paint()..color = nodeToColour[node]!;
final nodeOffset = Offset(position.x, position.y);
canvas.drawCircle(nodeOffset, 15, nodePaint);
// 绘制角色标签。请参阅jean_node_data.dart以获取此数据的源代码。
final textSpan = TextSpan(
text: nodeToName[node]!,
style: const TextStyle(color: Colors.black),
);
final textPainter = TextPainter(
text: textSpan,
textDirection: TextDirection.ltr,
);
textPainter.layout();
textPainter.paint(
canvas,
nodeOffset - const Offset(7.5, 10),
);
// TODO: 最终我们需要在这里调用`textPainter.dispose()`。
// 请参见https://github.com/flutter/flutter/blob/0b451b6dfd6de73ff89d89081c33d0f971db1872/packages/flutter/lib/src/painting/text_painter.dart#L171。
},
);
return _selectedDemoIndex == 2
? InteractiveGraph(
layoutAlgorithm: Eades(
graph: Graph.fromEdgeList(edges),
),
themePreferences: theme,
)
: StaticGraph(
layoutAlgorithm: FruchtermanReingold(
graph: Graph.fromEdgeList(edges),
),
themePreferences: theme,
);
} else if (snapshot.hasError) {
return const Center(child: Text('错误:无法获取图表数据'));
} else {
return const Center(child: CircularProgressIndicator());
}
},
);
default:
throw ArgumentError.value(_selectedDemoIndex, '_selectedDemoIndex');
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// 选择要显示的演示的下拉菜单。
DropdownButton(
value: _selectedDemoIndex,
onChanged: (value) {
setState(() {
_selectedDemoIndex = value!;
});
},
items: List.generate(
_demoNames.length,
(i) => DropdownMenuItem(
value: i,
child: Text(_demoNames[i]),
),
),
),
// 图表演示本身。
_displayDemo(),
],
),
),
);
}
}
更多关于Flutter图表展示插件graph_display的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter图表展示插件graph_display的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用graph_display
(假设这是一个用于图表展示的插件,虽然实际上Flutter社区中更常用的图表插件可能是fl_chart
或charts_flutter
,但这里我们按照要求以graph_display
为例)的示例代码。请注意,由于graph_display
可能不是一个真实存在的广泛使用的插件,这里的代码示例是基于假设的API设计。
首先,确保你已经在pubspec.yaml
文件中添加了graph_display
依赖:
dependencies:
flutter:
sdk: flutter
graph_display: ^1.0.0 # 假设的版本号
然后,运行flutter pub get
来安装依赖。
接下来,是一个简单的Flutter应用示例,展示如何使用graph_display
插件来展示一个图表:
import 'package:flutter/material.dart';
import 'package:graph_display/graph_display.dart'; // 假设的导入路径
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Graph Display Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: GraphDisplayScreen(),
);
}
}
class GraphDisplayScreen extends StatefulWidget {
@override
_GraphDisplayScreenState createState() => _GraphDisplayScreenState();
}
class _GraphDisplayScreenState extends State<GraphDisplayScreen> {
// 假设的数据
List<double> dataPoints = [10, 20, 15, 30, 25, 40, 35];
List<String> labels = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Graph Display Demo'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: GraphDisplay(
dataPoints: dataPoints,
labels: labels,
title: 'Sample Graph',
xAxisLabel: 'Categories',
yAxisLabel: 'Values',
// 假设的图表配置选项
barColor: Colors.blue,
backgroundColor: Colors.white,
gridColor: Colors.grey.shade300,
),
),
);
}
}
// 假设的 GraphDisplay 组件实现(实际使用时应该是插件提供的)
class GraphDisplay extends StatelessWidget {
final List<double> dataPoints;
final List<String> labels;
final String title;
final String xAxisLabel;
final String yAxisLabel;
final Color barColor;
final Color backgroundColor;
final Color gridColor;
GraphDisplay({
required this.dataPoints,
required this.labels,
required this.title,
required this.xAxisLabel,
required this.yAxisLabel,
this.barColor = Colors.blue,
this.backgroundColor = Colors.white,
this.gridColor = Colors.grey.shade300,
});
@override
Widget build(BuildContext context) {
// 注意:这里的实现是为了展示目的而编写的,实际插件可能有不同的API
return CustomPaint(
size: Size.fromWidthAndHeight(double.infinity, 300), // 假设的图表大小
painter: GraphPainter(
dataPoints: dataPoints,
labels: labels,
title: title,
xAxisLabel: xAxisLabel,
yAxisLabel: yAxisLabel,
barColor: barColor,
backgroundColor: backgroundColor,
gridColor: gridColor,
),
);
}
}
// 假设的 GraphPainter 类(实际使用时应该是插件提供的绘制逻辑)
class GraphPainter extends CustomPainter {
final List<double> dataPoints;
final List<String> labels;
final String title;
final String xAxisLabel;
final String yAxisLabel;
final Color barColor;
final Color backgroundColor;
final Color gridColor;
GraphPainter({
required this.dataPoints,
required this.labels,
required this.title,
required this.xAxisLabel,
required this.yAxisLabel,
required this.barColor,
required this.backgroundColor,
required this.gridColor,
});
@override
void paint(Canvas canvas, Size size) {
final Paint backgroundPaint = Paint()
..color = backgroundColor
..style = PaintingStyle.fill;
canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), backgroundPaint);
// 绘制标题、坐标轴标签等(省略具体实现细节)
// ...
// 绘制柱状图
final double barWidth = size.width / dataPoints.length * 0.8 / labels.length;
for (int i = 0; i < dataPoints.length; i++) {
final double x = i * (size.width / dataPoints.length * 1.2) + barWidth / 2;
final double y = size.height - dataPoints[i] * (size.height / dataPoints.reduce((a, b) => math.max(a, b)));
final Rect barRect = Rect.fromLTWH(x - barWidth / 2, y, barWidth, size.height - y);
final Paint barPaint = Paint()
..color = barColor
..style = PaintingStyle.fill;
canvas.drawRect(barRect, barPaint);
}
// 绘制网格线(省略具体实现细节)
// ...
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return oldDelegate != this;
}
}
请注意,上述代码中的GraphDisplay
和GraphPainter
类是为了演示目的而编写的,并不代表graph_display
插件的实际API。在实际使用中,你应该参考插件的官方文档来了解如何正确配置和使用该插件。如果graph_display
不是一个真实存在的插件,你可能需要考虑使用fl_chart
或charts_flutter
等广泛使用的Flutter图表插件。