|
|
|
|
import 'dart:math' as math;
|
|
|
|
|
|
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:pie_chart/pie_chart.dart';
|
|
|
|
|
|
|
|
|
|
class PieChartPainter extends CustomPainter {
|
|
|
|
|
List<Paint> _paintList = [];
|
|
|
|
|
List<double> _subParts;
|
|
|
|
|
List<String> _subTitles;
|
|
|
|
|
double _total = 0;
|
|
|
|
|
double _totalAngle = math.pi * 2;
|
|
|
|
|
|
|
|
|
|
final TextStyle chartValueStyle;
|
|
|
|
|
final Color chartValueBackgroundColor;
|
|
|
|
|
final double initialAngle;
|
|
|
|
|
final bool showValuesInPercentage;
|
|
|
|
|
final bool showChartValues;
|
|
|
|
|
final bool showChartValuesOutside;
|
|
|
|
|
final int decimalPlaces;
|
|
|
|
|
final bool showChartValueLabel;
|
|
|
|
|
final ChartType chartType;
|
|
|
|
|
final Function formatChartValues;
|
|
|
|
|
final double strokeWidth;
|
|
|
|
|
final bool selected;
|
|
|
|
|
final List<Color> colorList;
|
|
|
|
|
double _prevAngle = 0;
|
|
|
|
|
final TextStyle sideTextStyle;
|
|
|
|
|
|
|
|
|
|
PieChartPainter(
|
|
|
|
|
double angleFactor,
|
|
|
|
|
this.showChartValues,
|
|
|
|
|
this.showChartValuesOutside,
|
|
|
|
|
this.colorList, {
|
|
|
|
|
this.chartValueStyle,
|
|
|
|
|
this.chartValueBackgroundColor,
|
|
|
|
|
List<double> values,
|
|
|
|
|
List<String> titles,
|
|
|
|
|
this.initialAngle,
|
|
|
|
|
this.showValuesInPercentage,
|
|
|
|
|
this.decimalPlaces,
|
|
|
|
|
this.showChartValueLabel,
|
|
|
|
|
this.chartType,
|
|
|
|
|
this.formatChartValues,
|
|
|
|
|
this.strokeWidth,
|
|
|
|
|
this.selected = false,
|
|
|
|
|
this.sideTextStyle,
|
|
|
|
|
}) {
|
|
|
|
|
for (int i = 0; i < values.length; i++) {
|
|
|
|
|
final paint = Paint()..color = getColor(colorList, i);
|
|
|
|
|
if (chartType == ChartType.ring) {
|
|
|
|
|
paint.style = PaintingStyle.stroke;
|
|
|
|
|
paint.strokeWidth = strokeWidth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_paintList.add(paint);
|
|
|
|
|
}
|
|
|
|
|
_totalAngle = angleFactor * math.pi * 2;
|
|
|
|
|
_subParts = values;
|
|
|
|
|
_subTitles = titles;
|
|
|
|
|
_total = values.fold(0, (v1, v2) => v1 + v2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
void paint(Canvas canvas, Size size) {
|
|
|
|
|
final side = size.width < size.height ? size.width : size.height;
|
|
|
|
|
_prevAngle = initialAngle * math.pi / 180;
|
|
|
|
|
for (int i = 0; i < _subParts.length; i++) {
|
|
|
|
|
canvas.drawArc(
|
|
|
|
|
Rect.fromLTWH(0.0, 0.0, side, size.height),
|
|
|
|
|
_prevAngle,
|
|
|
|
|
(((_totalAngle) / _total) * _subParts[i]),
|
|
|
|
|
chartType == ChartType.disc ? true : false,
|
|
|
|
|
_paintList[i],
|
|
|
|
|
);
|
|
|
|
|
final radius = showChartValuesOutside ? (side / 2) + 16 : side / 3;
|
|
|
|
|
final x = (radius) * math.cos(_prevAngle + ((((_totalAngle) / _total) * _subParts[i]) / 2));
|
|
|
|
|
final y = (radius) * math.sin(_prevAngle + ((((_totalAngle) / _total) * _subParts[i]) / 2));
|
|
|
|
|
if (_subParts.elementAt(i).toInt() != 0) {
|
|
|
|
|
final value = formatChartValues != null ? formatChartValues(_subParts.elementAt(i)) : _subParts.elementAt(i).toStringAsFixed(this.decimalPlaces);
|
|
|
|
|
|
|
|
|
|
// final name = showValuesInPercentage
|
|
|
|
|
// ? (((_subParts.elementAt(i) / _total) * 100)
|
|
|
|
|
// .toStringAsFixed(this.decimalPlaces) +
|
|
|
|
|
// '%')
|
|
|
|
|
// : value;
|
|
|
|
|
// final name = showValuesInPercentage ? (((_subParts.elementAt(i) / _total) * 100).toStringAsFixed(this.decimalPlaces) + '%') : _subTitles.elementAt(i);
|
|
|
|
|
|
|
|
|
|
if (showChartValues) {
|
|
|
|
|
final name = "${_subTitles[i]} " + (showValuesInPercentage ? (((_subParts.elementAt(i) / _total) * 100).toStringAsFixed(this.decimalPlaces) + '%') : value);
|
|
|
|
|
var yLabel = y > size.width ? y + 40 : y + 10;
|
|
|
|
|
var xLabel = (y > size.width ? x - y : x * 1.6) - 15;
|
|
|
|
|
var yPopup = y - 8;
|
|
|
|
|
if (_subParts[i] <= (_total / _subParts.length)) {
|
|
|
|
|
yLabel = y + 8;
|
|
|
|
|
xLabel *= 1.1;
|
|
|
|
|
yPopup *= 1.25;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selected) _drawLabel(canvas, name, x, yPopup, side, true, statusColor: getColor(colorList, i));
|
|
|
|
|
_drawLabel(canvas, _subTitles[i], xLabel, yLabel, side + 8, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_prevAngle = _prevAngle + (((_totalAngle) / _total) * _subParts[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void _drawLabel(Canvas canvas, String name, double x, double y, double side, bool withBackground, {Color statusColor = Colors.white}) {
|
|
|
|
|
TextSpan span = TextSpan(
|
|
|
|
|
style: withBackground ? chartValueStyle : sideTextStyle,
|
|
|
|
|
text: name,
|
|
|
|
|
);
|
|
|
|
|
TextPainter tp = TextPainter(
|
|
|
|
|
text: span,
|
|
|
|
|
textAlign: TextAlign.center,
|
|
|
|
|
textDirection: TextDirection.ltr,
|
|
|
|
|
);
|
|
|
|
|
tp.layout();
|
|
|
|
|
|
|
|
|
|
if (withBackground && showChartValueLabel) {
|
|
|
|
|
//Draw text background box
|
|
|
|
|
final rect = Rect.fromCenter(
|
|
|
|
|
center: Offset((side / 2 + (x + 16)), (side / 2 + y)),
|
|
|
|
|
width: tp.width + 45,
|
|
|
|
|
height: tp.height + 16,
|
|
|
|
|
);
|
|
|
|
|
final rect2 = Rect.fromCenter(
|
|
|
|
|
center: Offset(
|
|
|
|
|
(side / 2 + x) - ((tp.width - 16) / 2),
|
|
|
|
|
(side / 2 + y) - ((tp.height - 16) / 2),
|
|
|
|
|
),
|
|
|
|
|
width: 12,
|
|
|
|
|
height: 12,
|
|
|
|
|
);
|
|
|
|
|
final rRect = RRect.fromRectAndRadius(rect, const Radius.circular(8));
|
|
|
|
|
final rRect2 = RRect.fromRectAndRadius(rect2, const Radius.circular(1));
|
|
|
|
|
final paint = Paint()
|
|
|
|
|
..color = chartValueBackgroundColor ?? Colors.grey[200]
|
|
|
|
|
..style = PaintingStyle.fill;
|
|
|
|
|
canvas.drawRRect(rRect, paint);
|
|
|
|
|
canvas.drawRRect(rRect2, paint..color = statusColor);
|
|
|
|
|
}
|
|
|
|
|
//Finally paint the text above box
|
|
|
|
|
tp.paint(
|
|
|
|
|
canvas,
|
|
|
|
|
Offset(
|
|
|
|
|
(side / 2 + x) - ((tp.width - 50) / 2),
|
|
|
|
|
(side / 2 + y) - ((tp.height) / 2),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@override
|
|
|
|
|
bool shouldRepaint(PieChartPainter oldDelegate) => selected != oldDelegate.selected;
|
|
|
|
|
}
|