findPieSegment static method
Find pie/donut chart segment at tap location.
Determines which segment of a pie or donut chart contains the tap position by calculating the angle from the center. Returns null if tap is outside the chart or in the center hole (for donut charts).
Parameters:
tapPosition- The position where the user tappeddata- List of pie data segmentssize- Size of the chart areacenterSpaceRadius- Inner radius for donut charts (0 for pie charts)
Returns a ChartInteractionResult if a segment is found, null otherwise.
Implementation
static ChartInteractionResult? findPieSegment(
Offset tapPosition,
List<PieData> data,
Size size,
double centerSpaceRadius,
) {
// Validate inputs
if (data.isEmpty) return null;
if (!size.width.isFinite ||
!size.height.isFinite ||
size.width <= 0 ||
size.height <= 0) {
return null;
}
if (!tapPosition.dx.isFinite || !tapPosition.dy.isFinite) {
return null;
}
final center = Offset(size.width / 2, size.height / 2);
final radius = math.min(size.width, size.height) / 2 - 20;
// Validate radius
if (radius <= 0 || !radius.isFinite) return null;
// Check if tap is within chart bounds
final distanceFromCenter = (tapPosition - center).distance;
if (!distanceFromCenter.isFinite) return null;
if (distanceFromCenter < centerSpaceRadius || distanceFromCenter > radius) {
return null;
}
final total = data.map((d) => d.value).reduce((a, b) => a + b);
if (total <= 0 || !total.isFinite) return null;
double startAngle = -math.pi / 2;
// Calculate angle from center to tap point
final dx = tapPosition.dx - center.dx;
final dy = tapPosition.dy - center.dy;
final tapAngle = math.atan2(dy, dx);
// Normalize to 0-2π range starting from top
final normalizedAngle =
(tapAngle + math.pi / 2 + 2 * math.pi) % (2 * math.pi);
for (int i = 0; i < data.length; i++) {
final item = data[i];
final sweepAngle = (item.value / total) * 2 * math.pi;
final endAngle = startAngle + sweepAngle;
// Normalize start and end angles
final normalizedStart = (startAngle + 2 * math.pi) % (2 * math.pi);
final normalizedEnd = (endAngle + 2 * math.pi) % (2 * math.pi);
// Check if tap angle is within segment
bool isInSegment = false;
if (normalizedEnd > normalizedStart) {
isInSegment = normalizedAngle >= normalizedStart &&
normalizedAngle <= normalizedEnd;
} else {
// Segment wraps around
isInSegment = normalizedAngle >= normalizedStart ||
normalizedAngle <= normalizedEnd;
}
if (isInSegment) {
return ChartInteractionResult(
segment: item,
elementIndex: i,
isHit: true,
);
}
startAngle = endAngle;
}
return null;
}