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
31 changes: 31 additions & 0 deletions lib/src/chart/radar_chart/radar_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class RadarChartData extends BaseChartData with EquatableMixin {
BorderSide? gridBorderData,
RadarTouchData? radarTouchData,
this.isMinValueAtCenter = false,
this.maxValue,
this.showMaxTick = false,
super.borderData,
}) : assert(dataSets != null && dataSets.hasEqualDataEntriesLength),
assert(
Expand All @@ -87,6 +89,10 @@ class RadarChartData extends BaseChartData with EquatableMixin {
titlePositionPercentageOffset <= 1,
'titlePositionPercentageOffset must be something between 0 and 1 ',
),
assert(
maxValue == null || maxValue.isFinite,
'maxValue must be a finite number',
),
dataSets = dataSets ?? const [],
radarBackgroundColor = radarBackgroundColor ?? Colors.transparent,
radarBorderData = radarBorderData ?? const BorderSide(width: 2),
Expand Down Expand Up @@ -160,12 +166,29 @@ class RadarChartData extends BaseChartData with EquatableMixin {
/// If [isMinValueAtCenter] is true, the minimum value of the [RadarChart] will be at the center of the chart.
final bool isMinValueAtCenter;

/// Custom maximum value for the [RadarChart]. If provided, this value will be used
/// instead of the automatically calculated maximum from the data.
/// This is useful for standardizing display across multiple charts or reserving space
/// for future data growth. If null, the maximum value will be calculated from the data.
final double? maxValue;

/// Whether to show the maximum tick value on the radar chart.
/// If true, displays the tick at the maximum value position.
/// If false (default), hides the maximum tick to avoid overlap with chart border.
final bool showMaxTick;

/// [titleCount] we use this value to determine number of [RadarChart] grid or lines.
int get titleCount => dataSets[0].dataEntries.length;

/// defines the maximum [RadarEntry] value in all [dataSets]
/// we use this value to calculate the maximum value of ticks.
RadarEntry get maxEntry {
// If custom maxValue is provided, use it
if (maxValue != null) {
return RadarEntry(value: maxValue!);
}

// Otherwise, calculate from data
var maximum = dataSets.first.dataEntries.first;

for (final dataSet in dataSets) {
Expand Down Expand Up @@ -206,6 +229,8 @@ class RadarChartData extends BaseChartData with EquatableMixin {
BorderSide? gridBorderData,
RadarTouchData? radarTouchData,
bool? isMinValueAtCenter,
double? maxValue,
bool? showMaxTick,
FlBorderData? borderData,
}) =>
RadarChartData(
Expand All @@ -223,6 +248,8 @@ class RadarChartData extends BaseChartData with EquatableMixin {
gridBorderData: gridBorderData ?? this.gridBorderData,
radarTouchData: radarTouchData ?? this.radarTouchData,
isMinValueAtCenter: isMinValueAtCenter ?? this.isMinValueAtCenter,
maxValue: maxValue ?? this.maxValue,
showMaxTick: showMaxTick ?? this.showMaxTick,
borderData: borderData ?? this.borderData,
);

Expand Down Expand Up @@ -250,6 +277,8 @@ class RadarChartData extends BaseChartData with EquatableMixin {
tickBorderData: BorderSide.lerp(a.tickBorderData, b.tickBorderData, t),
borderData: FlBorderData.lerp(a.borderData, b.borderData, t),
isMinValueAtCenter: b.isMinValueAtCenter,
maxValue: lerpDouble(a.maxValue, b.maxValue, t),
showMaxTick: b.showMaxTick,
radarTouchData: b.radarTouchData,
);
} else {
Expand All @@ -274,6 +303,8 @@ class RadarChartData extends BaseChartData with EquatableMixin {
gridBorderData,
radarTouchData,
isMinValueAtCenter,
maxValue,
showMaxTick,
];
}

Expand Down
6 changes: 5 additions & 1 deletion lib/src/chart/radar_chart/radar_chart_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,11 @@ class RadarChartPainter extends BaseChartPainter<RadarChartData> {
..strokeWidth = data.tickBorderData.width;

/// draw radar ticks
ticks.sublist(0, ticks.length - 1).asMap().forEach(
final ticksToShow = data.showMaxTick
? ticks // Show all ticks including max
: ticks.sublist(0, ticks.length - 1); // Hide max tick (current behavior)

ticksToShow.asMap().forEach(
(index, tick) {
final tickRadius =
tickDistance * (index + (data.isMinValueAtCenter ? 0 : 1));
Expand Down
96 changes: 96 additions & 0 deletions test/chart/radar_chart/radar_chart_data_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,31 @@ void main() {
),
false,
);

expect(
radarChartData1 == radarChartData1Clone.copyWith(maxValue: null),
true,
);

expect(
radarChartData1 == radarChartData1Clone.copyWith(maxValue: 100.0),
false,
);

expect(
radarChartData1 == radarChartData1Clone.copyWith(maxValue: 200.0),
false,
);

expect(
radarChartData1 == radarChartData1Clone.copyWith(showMaxTick: false),
true,
);

expect(
radarChartData1 == radarChartData1Clone.copyWith(showMaxTick: true),
false,
);
});

test('RadarDataSet equality test', () {
Expand Down Expand Up @@ -270,4 +295,75 @@ void main() {
expect(radarTouchedSpot1 == radarTouchedSpot7, false);
});
});

group('RadarChart maxValue functionality', () {
test('maxEntry returns data max when maxValue is null', () {
final data = RadarChartData(
dataSets: [radarDataSet1],
);
expect(data.maxEntry.value, equals(4.0));
});

test('maxEntry returns data max from multiple datasets when maxValue is null', () {
final data = RadarChartData(
dataSets: [radarDataSet1, radarDataSet2],
);
expect(data.maxEntry.value, equals(10.0));
});

test('maxEntry returns custom maxValue when provided', () {
final data = RadarChartData(
dataSets: [radarDataSet1],
maxValue: 150.0,
);
expect(data.maxEntry.value, equals(150.0));
});

test('maxValue validation throws on NaN', () {
expect(
() => RadarChartData(
dataSets: [radarDataSet1],
maxValue: double.nan,
),
throwsAssertionError,
);
});

test('maxValue validation throws on infinity', () {
expect(
() => RadarChartData(
dataSets: [radarDataSet1],
maxValue: double.infinity,
),
throwsAssertionError,
);
});
});

group('RadarChart showMaxTick functionality', () {
test('showMaxTick defaults to false and preserves original behavior', () {
final data = RadarChartData(
dataSets: [radarDataSet1],
);
expect(data.showMaxTick, equals(false));
});

test('showMaxTick can be set to true to enable max tick display', () {
final data = RadarChartData(
dataSets: [radarDataSet1],
showMaxTick: true,
);
expect(data.showMaxTick, equals(true));
});

test('showMaxTick works correctly with custom maxValue', () {
final data = RadarChartData(
dataSets: [radarDataSet1],
maxValue: 200.0,
showMaxTick: true,
);
expect(data.showMaxTick, equals(true));
expect(data.maxEntry.value, equals(200.0));
});
});
}