Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions lib/src/chart_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class PieChartPainter extends CustomPainter {
final DegreeOptions degreeOptions;
final Color baseChartColor;
final double? totalValue;
final void Function(String)? onSegmentPressed;

late double _prevAngle;

Expand Down Expand Up @@ -57,6 +58,7 @@ class PieChartPainter extends CustomPainter {
this.degreeOptions = const DegreeOptions(),
required this.baseChartColor,
this.totalValue,
this.onSegmentPressed,
}) {
// set total value
if (totalValue == null) {
Expand Down Expand Up @@ -98,7 +100,10 @@ class PieChartPainter extends CustomPainter {
// this is not a precise calculation, should be more generalized later
// e.g. (-90, 91) should start from the left

final left = degreeOptions.initialAngle >= -90 && degreeOptions.totalDegrees <= 180 ? -size.width / 2 : 0.0;
final left =
degreeOptions.initialAngle >= -90 && degreeOptions.totalDegrees <= 180
? -size.width / 2
: 0.0;

const top = 0.0;

Expand Down Expand Up @@ -131,7 +136,8 @@ class PieChartPainter extends CustomPainter {
);
} else {
final isGradientPresent = gradientList?.isNotEmpty ?? false;
final isNonGradientElementPresent = (_subParts.length - (gradientList?.length ?? 0)) > 0;
final isNonGradientElementPresent =
(_subParts.length - (gradientList?.length ?? 0)) > 0;

for (int i = 0; i < _subParts.length; i++) {
if (isGradientPresent) {
Expand All @@ -144,7 +150,8 @@ class PieChartPainter extends CustomPainter {
transform: GradientRotation(normalizedPrevAngle),
endAngle: normalizedEndAngle,
colors: getGradient(gradientList!, i,
isNonGradientElementPresent: isNonGradientElementPresent, emptyColorGradient: emptyColorGradient!),
isNonGradientElementPresent: isNonGradientElementPresent,
emptyColorGradient: emptyColorGradient!),
);
paint.shader = gradient.createShader(boundingSquare);
if (chartType == ChartType.ring) {
Expand All @@ -170,7 +177,8 @@ class PieChartPainter extends CustomPainter {
}

final radius = showChartValuesOutside ? (side / 2) + 16 : side / 3;
final radians = _prevAngle + (((_totalAngle / _total) * _subParts[i]) / 2);
final radians =
_prevAngle + (((_totalAngle / _total) * _subParts[i]) / 2);
final x = (radius) * math.cos(radians);
final y = (radius) * math.sin(radians);

Expand Down Expand Up @@ -244,5 +252,6 @@ class PieChartPainter extends CustomPainter {
}

@override
bool shouldRepaint(PieChartPainter oldDelegate) => oldDelegate._totalAngle != _totalAngle;
bool shouldRepaint(PieChartPainter oldDelegate) =>
oldDelegate._totalAngle != _totalAngle;
}
145 changes: 100 additions & 45 deletions lib/src/pie_chart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:pie_chart/pie_chart.dart';
import 'chart_painter.dart';
import 'legend.dart';
import 'utils.dart';
import 'dart:math';

enum LegendPosition { top, bottom, left, right }

Expand Down Expand Up @@ -33,6 +34,7 @@ class PieChart extends StatefulWidget {
this.degreeOptions = const DegreeOptions(),
this.baseChartColor = Colors.transparent,
this.totalValue,
this.onSegmentPressed,
}) : super(key: key);

final Map<String, double> dataMap;
Expand All @@ -58,6 +60,7 @@ class PieChart extends StatefulWidget {
final Map<String, String> legendLabels;
final Color baseChartColor;
final double? totalValue;
final void Function(String)? onSegmentPressed;

@override
// ignore: library_private_types_in_public_api
Expand Down Expand Up @@ -119,51 +122,64 @@ class _PieChartState extends State<PieChart>
Widget _getChart() {
return Flexible(
child: LayoutBuilder(
builder: (_, c) => SizedBox(
height: widget.chartRadius != null
? c.maxWidth < widget.chartRadius!
? c.maxWidth
: widget.chartRadius
: null,
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: PieChartPainter(
_animFraction,
widget.chartValuesOptions.showChartValues,
widget.chartValuesOptions.showChartValuesOutside,
widget.colorList,
chartValueStyle: widget.chartValuesOptions.chartValueStyle,
chartValueBackgroundColor:
widget.chartValuesOptions.chartValueBackgroundColor,
values: legendValues,
titles: legendTitles,
showValuesInPercentage:
widget.chartValuesOptions.showChartValuesInPercentage,
decimalPlaces: widget.chartValuesOptions.decimalPlaces,
showChartValueLabel:
widget.chartValuesOptions.showChartValueBackground,
chartType: widget.chartType,
centerText: widget.centerText,
centerTextStyle: widget.centerTextStyle,
formatChartValues: widget.formatChartValues,
strokeWidth: widget.ringStrokeWidth,
emptyColor: widget.emptyColor,
gradientList: widget.gradientList,
emptyColorGradient: widget.emptyColorGradient,
degreeOptions: widget.degreeOptions.copyWith(
// because we've deprecated initialAngleInDegree,
// we want the old value to be used if it's not null
// ignore: deprecated_member_use_from_same_package
initialAngle: widget.initialAngleInDegree,
),
baseChartColor: widget.baseChartColor,
totalValue: widget.totalValue),
child: const AspectRatio(aspectRatio: 1),
),
if (widget.centerWidget != null) widget.centerWidget!
],
builder: (_, c) => GestureDetector(
onTapDown: (details) {
if (widget.onSegmentPressed == null) return;
final size = context.size;
final renderBox = context.findRenderObject() as RenderBox?;
if (size == null || renderBox == null) return;
final position = renderBox.globalToLocal(details.globalPosition);
final key = _detectTappedSegment(position, size);
if (key == null) return;
widget.onSegmentPressed?.call(key);
},
child: SizedBox(
height: widget.chartRadius != null
? c.maxWidth < widget.chartRadius!
? c.maxWidth
: widget.chartRadius
: null,
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
painter: PieChartPainter(
_animFraction,
widget.chartValuesOptions.showChartValues,
widget.chartValuesOptions.showChartValuesOutside,
widget.colorList,
chartValueStyle:
widget.chartValuesOptions.chartValueStyle,
chartValueBackgroundColor:
widget.chartValuesOptions.chartValueBackgroundColor,
values: legendValues,
titles: legendTitles,
showValuesInPercentage:
widget.chartValuesOptions.showChartValuesInPercentage,
decimalPlaces: widget.chartValuesOptions.decimalPlaces,
showChartValueLabel:
widget.chartValuesOptions.showChartValueBackground,
chartType: widget.chartType,
centerText: widget.centerText,
centerTextStyle: widget.centerTextStyle,
formatChartValues: widget.formatChartValues,
strokeWidth: widget.ringStrokeWidth,
emptyColor: widget.emptyColor,
gradientList: widget.gradientList,
emptyColorGradient: widget.emptyColorGradient,
degreeOptions: widget.degreeOptions.copyWith(
// because we've deprecated initialAngleInDegree,
// we want the old value to be used if it's not null
// ignore: deprecated_member_use_from_same_package
initialAngle: widget.initialAngleInDegree,
),
baseChartColor: widget.baseChartColor,
totalValue: widget.totalValue),
child: const AspectRatio(aspectRatio: 1),
),
if (widget.centerWidget != null) widget.centerWidget!
],
),
),
),
),
Expand Down Expand Up @@ -278,6 +294,45 @@ class _PieChartState extends State<PieChart>
}
}

String? _detectTappedSegment(Offset position, Size size) {
final Offset center = size.center(Offset.zero);
final dx = position.dx - center.dx;
final dy = position.dy - center.dy;
final distance = sqrt(dx * dx + dy * dy);

final radius = size.shortestSide / 2;
if (distance > radius) return null;

double angle = atan2(dy, dx);
if (angle < 0) angle += 2 * pi;

final total = widget.dataMap.values.reduce((a, b) => a + b);

double startAngle = degreeToRadian(
widget.degreeOptions.initialAngle,
);

final keys = widget.dataMap.keys.toList();
final values = widget.dataMap.values.toList();

for (int i = 0; i < values.length; i++) {
final sweepAngle = (values[i] / total) * 2 * pi;

final endAngle = startAngle + sweepAngle;
final normalizedAngle = angle;

if (normalizedAngle >= startAngle && normalizedAngle <= endAngle) {
return keys[i];
}

startAngle = endAngle;
}

return null;
}

double degreeToRadian(double degree) => degree * pi / 180;

@override
Widget build(BuildContext context) {
return Container(
Expand Down