如何在Flutter中实现Pie Chart与Line Chart以实现数据可视化
Flutter饼图与折线图实现指南
一、使用第三方图表库(推荐)
第三方库是最省心的方案,推荐fl_chart,它轻量、定制性强,性能表现稳定,支持饼图、折线图等多种图表类型。
1. 饼图实现
先在pubspec.yaml中添加依赖:
dependencies: fl_chart: ^0.65.0
示例代码:
import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; class PieChartSample extends StatelessWidget { @override Widget build(BuildContext context) { return SizedBox( height: 300, child: PieChart( PieChartData( sections: [ PieChartSectionData( color: Colors.blue, value: 30, title: '30%', radius: 80, ), PieChartSectionData( color: Colors.green, value: 40, title: '40%', radius: 80, ), PieChartSectionData( color: Colors.orange, value: 30, title: '30%', radius: 80, ), ], centerSpaceRadius: 40, // 中间空白区域半径 ), ), ); } }
通过PieChartSectionData可自定义扇区颜色、大小、标签,还能添加点击事件、过渡动画等效果。
2. 折线图实现
同样基于fl_chart的示例代码:
import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; class LineChartSample extends StatelessWidget { @override Widget build(BuildContext context) { return SizedBox( height: 300, child: LineChart( LineChartData( gridData: FlGridData(show: true), // 显示网格线 titlesData: FlTitlesData( leftTitles: AxisTitles(sideTitles: SideTitles(showTitles: true)), // 左侧刻度标签 bottomTitles: AxisTitles(sideTitles: SideTitles(showTitles: true)), // 底部刻度标签 ), borderData: FlBorderData(show: true), // 显示图表边框 lineBarsData: [ LineChartBarData( spots: [ FlSpot(0, 5), FlSpot(1, 10), FlSpot(2, 7), FlSpot(3, 12), FlSpot(4, 9), ], isCurved: true, // 开启曲线平滑效果 color: Colors.blue, barWidth: 3, dotData: FlDotData(show: true), // 显示数据点 ), ], ), ), ); } }
支持自定义曲线弧度、数据点样式、坐标轴标签,还能添加多条折线、区域填充等进阶效果。
二、CustomPaint手动绘制
如果需要高度定制化或不想依赖第三方库,可通过CustomPaint手动实现。
1. 饼图手动绘制
核心逻辑是计算每个扇区的角度,再用Canvas绘制圆弧:
class CustomPieChart extends StatelessWidget { final List<double> values = [30, 40, 30]; final List<Color> colors = [Colors.blue, Colors.green, Colors.orange]; @override Widget build(BuildContext context) { return SizedBox( height: 300, width: 300, child: CustomPaint( painter: PiePainter(values, colors), ), ); } } class PiePainter extends CustomPainter { final List<double> values; final List<Color> colors; double total = 0; PiePainter(this.values, this.colors) { total = values.reduce((a, b) => a + b); } @override void paint(Canvas canvas, Size size) { double startAngle = 0; final center = Offset(size.width / 2, size.height / 2); final radius = size.width / 2 - 20; for (int i = 0; i < values.length; i++) { double sweepAngle = (values[i] / total) * 2 * 3.14159; final paint = Paint()..color = colors[i]; canvas.drawArc( Rect.fromCircle(center: center, radius: radius), startAngle, sweepAngle, true, paint, ); startAngle += sweepAngle; } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }
可扩展添加扇区标签、点击交互、角度动画等效果,比如结合AnimationController实现加载动画。
2. 折线图手动绘制
核心是将数据映射为坐标点,再用Canvas绘制线条和数据点:
class CustomLineChart extends StatelessWidget { final List<double> data = [5, 10, 7, 12, 9]; @override Widget build(BuildContext context) { return SizedBox( height: 300, width: double.infinity, child: CustomPaint( painter: LinePainter(data), ), ); } } class LinePainter extends CustomPainter { final List<double> data; LinePainter(this.data); @override void paint(Canvas canvas, Size size) { final padding = 20.0; final chartWidth = size.width - 2 * padding; final chartHeight = size.height - 2 * padding; final maxValue = data.reduce((a, b) => a > b ? a : b); // 绘制网格线 final gridPaint = Paint() ..color = Colors.grey.withOpacity(0.3) ..strokeWidth = 1; for (int i = 0; i <= 5; i++) { double y = padding + (chartHeight / 5) * i; canvas.drawLine(Offset(padding, y), Offset(size.width - padding, y), gridPaint); } // 计算数据点坐标 List<Offset> points = []; for (int i = 0; i < data.length; i++) { double x = padding + (chartWidth / (data.length - 1)) * i; double y = padding + chartHeight - (data[i] / maxValue) * chartHeight; points.add(Offset(x, y)); } // 绘制折线 final linePaint = Paint() ..color = Colors.blue ..strokeWidth = 3 ..style = PaintingStyle.stroke; canvas.drawPoints(PointMode.polygon, points, linePaint); // 绘制数据点 final dotPaint = Paint()..color = Colors.blue; for (var point in points) { canvas.drawCircle(point, 5, dotPaint); } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) => true; }
可扩展添加坐标轴标签、数值提示框、渐变填充等效果。
三、性能优化小技巧
- 使用第三方库时,避免频繁触发图表刷新,比如用
const修饰无状态组件,或仅在数据变化时调用setState。 - 手动绘制时,缓存计算好的坐标、路径等数据,避免每次
paint都重复计算。 - 处理大数据量时,优先选择支持数据采样的库,或手动对数据进行降采样处理。
内容的提问来源于stack exchange,提问作者Navya Narayanan
相关产品推荐
相关产品推荐

