Skip to content
Merged
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
2 changes: 2 additions & 0 deletions example/lib/presentation/samples/chart_samples.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import 'line/line_chart_sample9.dart';
import 'pie/pie_chart_sample1.dart';
import 'pie/pie_chart_sample2.dart';
import 'pie/pie_chart_sample3.dart';
import 'pie/pie_chart_sample4.dart';
import 'radar/radar_chart_sample1.dart';
import 'scatter/scatter_chart_sample1.dart';
import 'scatter/scatter_chart_sample2.dart';
Expand Down Expand Up @@ -61,6 +62,7 @@ class ChartSamples {
PieChartSample(1, (context) => const PieChartSample1()),
PieChartSample(2, (context) => const PieChartSample2()),
PieChartSample(3, (context) => const PieChartSample3()),
PieChartSample(4, (context) => const PieChartSample4()),
],
ChartType.scatter: [
ScatterChartSample(1, (context) => ScatterChartSample1()),
Expand Down
187 changes: 187 additions & 0 deletions example/lib/presentation/samples/pie/pie_chart_sample4.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:fl_chart_app/presentation/resources/app_colors.dart';
import 'package:flutter/material.dart';

class PieChartSample4 extends StatefulWidget {
const PieChartSample4({super.key});

@override
State<PieChartSample4> createState() => _PieChartSample4State();
}

class _PieChartSample4State extends State<PieChartSample4> {
int touchedIndex = 0;

@override
Widget build(BuildContext context) {
return AspectRatio(
aspectRatio: 1.3,
child: AspectRatio(
aspectRatio: 1,
child: PieChart(
PieChartData(
pieTouchData: PieTouchData(
touchCallback: (FlTouchEvent event, pieTouchResponse) {
setState(() {
if (!event.isInterestedForInteractions ||
pieTouchResponse == null ||
pieTouchResponse.touchedSection == null) {
touchedIndex = -1;
return;
}
touchedIndex =
pieTouchResponse.touchedSection!.touchedSectionIndex;
});
},
),
borderData: FlBorderData(
show: false,
),
sectionsSpace: 2,
centerSpaceRadius: 0,
sections: showingSections(),
),
),
),
);
}

List<PieChartSectionData> showingSections() {
return List.generate(5, (i) {
final isTouched = i == touchedIndex;
final fontSize = isTouched ? 20.0 : 16.0;
const shadows = [Shadow(color: Colors.black, blurRadius: 2)];
final value = 90.0;
final scaleFactor = isTouched ? 1.05 : 1.0;

return switch (i) {
0 => PieChartSectionData(
color: AppColors.contentColorGreen,
value: value,
title: 'A',
radius: 100 * scaleFactor,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff),
shadows: shadows,
),
titlePositionPercentageOffset: 0.75,
segments: [
PieChartStackSegmentData(
fromRadius: 30 * scaleFactor,
toRadius: 32 * scaleFactor,
color: AppColors.pageBackground,
),
PieChartStackSegmentData(
fromRadius: 32 * scaleFactor,
toRadius: 100 * scaleFactor,
color: AppColors.contentColorRed,
),
],
),
1 => PieChartSectionData(
color: AppColors.contentColorGreen,
value: value,
title: 'B',
radius: 100 * scaleFactor,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff),
shadows: shadows,
),
titlePositionPercentageOffset: 0.75,
segments: [
PieChartStackSegmentData(
fromRadius: 65 * scaleFactor,
toRadius: 67 * scaleFactor,
color: AppColors.pageBackground,
),
PieChartStackSegmentData(
fromRadius: 67 * scaleFactor,
toRadius: 100 * scaleFactor,
color: AppColors.contentColorRed,
),
],
),
2 => PieChartSectionData(
color: AppColors.contentColorGreen,
value: value,
title: 'C',
radius: 100 * scaleFactor,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff),
shadows: shadows,
),
titlePositionPercentageOffset: 0.75,
segments: [
PieChartStackSegmentData(
fromRadius: 50 * scaleFactor,
toRadius: 52 * scaleFactor,
color: AppColors.pageBackground,
),
PieChartStackSegmentData(
fromRadius: 52 * scaleFactor,
toRadius: 100 * scaleFactor,
color: AppColors.contentColorRed,
),
],
),
3 => PieChartSectionData(
color: AppColors.contentColorGreen,
value: value,
title: 'D',
radius: 100 * scaleFactor,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff),
shadows: shadows,
),
titlePositionPercentageOffset: 0.75,
segments: [
PieChartStackSegmentData(
fromRadius: 90 * scaleFactor,
toRadius: 92 * scaleFactor,
color: AppColors.pageBackground,
),
PieChartStackSegmentData(
fromRadius: 92 * scaleFactor,
toRadius: 100 * scaleFactor,
color: AppColors.contentColorRed,
),
],
),
4 => PieChartSectionData(
color: AppColors.contentColorGreen,
value: value,
title: 'E',
radius: 100 * scaleFactor,
titleStyle: TextStyle(
fontSize: fontSize,
fontWeight: FontWeight.bold,
color: const Color(0xffffffff),
shadows: shadows,
),
titlePositionPercentageOffset: 0.75,
segments: [
PieChartStackSegmentData(
fromRadius: 70 * scaleFactor,
toRadius: 72 * scaleFactor,
color: AppColors.pageBackground,
),
PieChartStackSegmentData(
fromRadius: 72 * scaleFactor,
toRadius: 100 * scaleFactor,
color: AppColors.contentColorRed,
),
],
),
_ => throw StateError('Invalid'),
};
});
}
}
113 changes: 109 additions & 4 deletions lib/src/chart/pie_chart/pie_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,8 @@ class PieChartData extends BaseChartData with EquatableMixin {
final bool titleSunbeamLayout;

/// We hold this value to determine weight of each [PieChartSectionData.value].
double get sumValue => sections
.map((data) => data.value)
.reduce((first, second) => first + second);
double get sumValue =>
sections.fold<double>(0, (sumVal, second) => sumVal + second.value);

/// Copies current [PieChartData] to a new [PieChartData],
/// and replaces provided values.
Expand Down Expand Up @@ -166,6 +165,7 @@ class PieChartSectionData with EquatableMixin {
this.badgeWidget,
double? titlePositionPercentageOffset,
double? badgePositionPercentageOffset,
List<PieChartStackSegmentData>? segments,
}) : value = value ?? 10,
color = color ?? Colors.cyan,
radius = (radius ?? 40).clamp(0, double.infinity).toDouble(),
Expand All @@ -175,7 +175,8 @@ class PieChartSectionData with EquatableMixin {
cornerRadius =
(cornerRadius ?? 0.0).clamp(0, double.infinity).toDouble(),
titlePositionPercentageOffset = titlePositionPercentageOffset ?? 0.5,
badgePositionPercentageOffset = badgePositionPercentageOffset ?? 0.5;
badgePositionPercentageOffset = badgePositionPercentageOffset ?? 0.5,
segments = segments ?? const [];

/// It determines how much space it should occupy around the circle.
///
Expand Down Expand Up @@ -229,6 +230,13 @@ class PieChartSectionData with EquatableMixin {
/// 1.0 means near the outside of the [PieChart].
final double badgePositionPercentageOffset;

/// Stacked segments rendered on top of the main section.
///
/// Each segment defines [PieChartStackSegmentData.fromRadius] and
/// [PieChartStackSegmentData.toRadius] to specify its position within
/// the section's [radius]. Values are clamped to [0, radius] at render time.
final List<PieChartStackSegmentData> segments;

/// Copies current [PieChartSectionData] to a new [PieChartSectionData],
/// and replaces provided values.
PieChartSectionData copyWith({
Expand All @@ -244,6 +252,7 @@ class PieChartSectionData with EquatableMixin {
Widget? badgeWidget,
double? titlePositionPercentageOffset,
double? badgePositionPercentageOffset,
List<PieChartStackSegmentData>? segments,
}) =>
PieChartSectionData(
value: value ?? this.value,
Expand All @@ -260,6 +269,7 @@ class PieChartSectionData with EquatableMixin {
titlePositionPercentageOffset ?? this.titlePositionPercentageOffset,
badgePositionPercentageOffset:
badgePositionPercentageOffset ?? this.badgePositionPercentageOffset,
segments: segments ?? this.segments,
);

/// Lerps a [PieChartSectionData] based on [t] value, check [Tween.lerp].
Expand Down Expand Up @@ -289,6 +299,11 @@ class PieChartSectionData with EquatableMixin {
b.badgePositionPercentageOffset,
t,
),
segments: lerpPieChartStackSegmentDataList(
a.segments,
b.segments,
t,
),
);

/// Used for equality check, see [EquatableMixin].
Expand All @@ -306,6 +321,96 @@ class PieChartSectionData with EquatableMixin {
badgeWidget,
titlePositionPercentageOffset,
badgePositionPercentageOffset,
segments,
];
}

/// A stylized segment of a Stacked Pie Chart section item.
///
/// Each [PieChartSectionData] can have a list of [PieChartStackSegmentData]
/// that are rendered on top of the main section, similar to how
/// [BarChartRodStackItem] works in bar charts.
///
/// Each segment defines [fromRadius] and [toRadius] to specify its position
/// within the section's total [PieChartSectionData.radius]. Values are clamped
/// to the valid range [0, sectionRadius].
class PieChartStackSegmentData with EquatableMixin {
/// Renders a segment of Stacked Pie Chart with given [fromRadius], [toRadius]
/// and [color] or [gradient].
///
/// For example, to have a section with a colored overlay in the outer half:
///
/// ```dart
/// PieChartSectionData(
/// color: Colors.red,
/// radius: 100,
/// segments: [
/// PieChartStackSegmentData(
/// fromRadius: 50,
/// toRadius: 100,
/// color: Colors.green,
/// ),
/// ]
/// )
/// ```
/// If [gradient] is specified, it overrides the [color] setting.
PieChartStackSegmentData({
double? fromRadius,
required this.toRadius,
Color? color,
this.gradient,
}) : fromRadius = fromRadius ?? 0,
color = color ?? Colors.purple;

/// The start radius of this segment (distance from center of the section).
/// Clamped to [0, sectionRadius] at render time.
final double fromRadius;

/// The end radius of this segment (distance from center of the section).
/// Clamped to [0, sectionRadius] at render time.
final double toRadius;

/// Defines the color of segment.
final Color color;

/// Defines the gradient of segment. If specified, overrides the color setting.
final Gradient? gradient;

/// Copies current [PieChartStackSegmentData] to a new [PieChartStackSegmentData],
/// and replaces provided values.
PieChartStackSegmentData copyWith({
double? fromRadius,
double? toRadius,
Color? color,
Gradient? gradient,
}) =>
PieChartStackSegmentData(
fromRadius: fromRadius ?? this.fromRadius,
toRadius: toRadius ?? this.toRadius,
color: color ?? this.color,
gradient: gradient ?? this.gradient,
);

/// Lerps a [PieChartStackSegmentData] based on [t] value, check [Tween.lerp].
static PieChartStackSegmentData lerp(
PieChartStackSegmentData a,
PieChartStackSegmentData b,
double t,
) =>
PieChartStackSegmentData(
fromRadius: lerpDouble(a.fromRadius, b.fromRadius, t),
toRadius: lerpDouble(a.toRadius, b.toRadius, t)!,
color: lerpColor(a.color, b.color, t),
gradient: Gradient.lerp(a.gradient, b.gradient, t),
);

/// Used for equality check, see [EquatableMixin].
@override
List<Object?> get props => [
fromRadius,
toRadius,
color,
gradient,
];
}

Expand Down
Loading
Loading