Skip to content

Commit

Permalink
Implement error indicator in bar chart. (sample 8)
Browse files Browse the repository at this point in the history
  • Loading branch information
imaNNeo committed Jan 10, 2025
1 parent 8c9596a commit f652121
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 4 deletions.
37 changes: 33 additions & 4 deletions example/lib/presentation/samples/bar/bar_chart_sample8.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,14 @@ class BarChartSample1State extends State<BarChartSample8> {
BarChartGroupData makeGroupData(
int x,
double y,
FlErrorRange errorRange,
) {
return BarChartGroupData(
x: x,
barRods: [
BarChartRodData(
toY: y,
toYErrorRange: errorRange,
color: x >= 4 ? Colors.transparent : widget.barColor,
borderRadius: BorderRadius.zero,
borderDashArray: x >= 4 ? [4, 4] : null,
Expand Down Expand Up @@ -134,12 +136,39 @@ class BarChartSample1State extends State<BarChartSample8> {
),
barGroups: List.generate(
7,
(i) => makeGroupData(
i,
Random().nextInt(290).toDouble() + 10,
),
(i) {
final y = Random().nextInt(290).toDouble() + 10;
final lowerBy = y < 50
? Random().nextDouble() * 10
: Random().nextDouble() * 30 + 5;
final upperBy = y > 290
? Random().nextDouble() * 10
: Random().nextDouble() * 30 + 5;
return makeGroupData(
i,
y,
FlErrorRange(
lowerBy: lowerBy,
upperBy: upperBy,
),
);
},
),
gridData: const FlGridData(show: false),
errorIndicatorData: FlErrorIndicatorData(
painter: _errorPainter,
),
);
}

FlSpotErrorRangePainter _errorPainter(
BarChartSpotErrorRangeCallbackInput input,
) =>
FlSimpleErrorPainter(
lineWidth: 2.0,
capLength: 14,
lineColor: input.groupIndex < 4
? AppColors.contentColorOrange
: AppColors.primary.withValues(alpha: 0.5),
);
}
44 changes: 44 additions & 0 deletions lib/src/chart/bar_chart/bar_chart_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class BarChartData extends AxisChartData with EquatableMixin {
super.backgroundColor,
ExtraLinesData? extraLinesData,
super.rotationQuarterTurns,
this.errorIndicatorData = const FlErrorIndicatorData(),
}) : barGroups = barGroups ?? const [],
groupsSpace = groupsSpace ?? 16,
alignment = alignment ?? BarChartAlignment.spaceEvenly,
Expand Down Expand Up @@ -79,6 +80,10 @@ class BarChartData extends AxisChartData with EquatableMixin {
/// Handles touch behaviors and responses.
final BarTouchData barTouchData;

/// Holds data for showing error indicators on the spots in this line.
final FlErrorIndicatorData<BarChartSpotErrorRangeCallbackInput>
errorIndicatorData;

/// Copies current [BarChartData] to a new [BarChartData],
/// and replaces provided values.
BarChartData copyWith({
Expand All @@ -96,6 +101,8 @@ class BarChartData extends AxisChartData with EquatableMixin {
Color? backgroundColor,
ExtraLinesData? extraLinesData,
int? rotationQuarterTurns,
FlErrorIndicatorData<BarChartSpotErrorRangeCallbackInput>?
errorIndicatorData,
}) =>
BarChartData(
barGroups: barGroups ?? this.barGroups,
Expand All @@ -112,6 +119,7 @@ class BarChartData extends AxisChartData with EquatableMixin {
backgroundColor: backgroundColor ?? this.backgroundColor,
extraLinesData: extraLinesData ?? this.extraLinesData,
rotationQuarterTurns: rotationQuarterTurns ?? this.rotationQuarterTurns,
errorIndicatorData: errorIndicatorData ?? this.errorIndicatorData,
);

/// Lerps a [BaseChartData] based on [t] value, check [Tween.lerp].
Expand All @@ -135,6 +143,11 @@ class BarChartData extends AxisChartData with EquatableMixin {
extraLinesData:
ExtraLinesData.lerp(a.extraLinesData, b.extraLinesData, t),
rotationQuarterTurns: b.rotationQuarterTurns,
errorIndicatorData: FlErrorIndicatorData.lerp(
a.errorIndicatorData,
b.errorIndicatorData,
t,
),
);
} else {
throw Exception('Illegal State');
Expand All @@ -158,6 +171,7 @@ class BarChartData extends AxisChartData with EquatableMixin {
backgroundColor,
extraLinesData,
rotationQuarterTurns,
errorIndicatorData,
];
}

Expand Down Expand Up @@ -318,6 +332,7 @@ class BarChartRodData with EquatableMixin {
BarChartRodData({
double? fromY,
required this.toY,
this.toYErrorRange,
Color? color,
this.gradient,
double? width,
Expand All @@ -341,6 +356,8 @@ class BarChartRodData with EquatableMixin {
/// [BarChart] renders rods vertically from [fromY] to [toY].
final double toY;

final FlErrorRange? toYErrorRange;

/// If provided, this [BarChartRodData] draws with this [color]
/// Otherwise we use [gradient] to draw the background.
/// It throws an exception if you provide both [color] and [gradient]
Expand Down Expand Up @@ -380,6 +397,7 @@ class BarChartRodData with EquatableMixin {
BarChartRodData copyWith({
double? fromY,
double? toY,
FlErrorRange? toYErrorRange,
Color? color,
Gradient? gradient,
double? width,
Expand All @@ -392,6 +410,7 @@ class BarChartRodData with EquatableMixin {
BarChartRodData(
fromY: fromY ?? this.fromY,
toY: toY ?? this.toY,
toYErrorRange: toYErrorRange ?? this.toYErrorRange,
color: color ?? this.color,
gradient: gradient ?? this.gradient,
width: width ?? this.width,
Expand All @@ -413,6 +432,7 @@ class BarChartRodData with EquatableMixin {
borderSide: BorderSide.lerp(a.borderSide, b.borderSide, t),
fromY: lerpDouble(a.fromY, b.fromY, t),
toY: lerpDouble(a.toY, b.toY, t)!,
toYErrorRange: FlErrorRange.lerp(a.toYErrorRange, b.toYErrorRange, t),
backDrawRodData: BackgroundBarChartRodData.lerp(
a.backDrawRodData,
b.backDrawRodData,
Expand All @@ -427,6 +447,7 @@ class BarChartRodData with EquatableMixin {
List<Object?> get props => [
fromY,
toY,
toYErrorRange,
width,
borderRadius,
borderDashArray,
Expand Down Expand Up @@ -929,6 +950,29 @@ class BarTouchedSpot extends TouchedSpot with EquatableMixin {
];
}

class BarChartSpotErrorRangeCallbackInput
extends FlSpotErrorRangeCallbackInput {
BarChartSpotErrorRangeCallbackInput({
required this.group,
required this.groupIndex,
required this.rod,
required this.barRodIndex,
});

final BarChartGroupData group;
final int groupIndex;
final BarChartRodData rod;
final int barRodIndex;

@override
List<Object?> get props => [
group,
groupIndex,
rod,
barRodIndex,
];
}

/// It lerps a [BarChartData] to another [BarChartData] (handles animation for updating values)
class BarChartDataTween extends Tween<BarChartData> {
BarChartDataTween({required BarChartData begin, required BarChartData end})
Expand Down
73 changes: 73 additions & 0 deletions lib/src/chart/bar_chart/bar_chart_painter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class BarChartPainter extends AxisChartPainter<BarChartData> {

_clipPaint = Paint();
}

late Paint _barPaint;
late Paint _barStrokePaint;
late Paint _bgTouchTooltipPaint;
Expand Down Expand Up @@ -93,6 +94,8 @@ class BarChartPainter extends AxisChartPainter<BarChartData> {

drawBars(canvasWrapper, _groupBarsPosition!, holder);

drawErrorIndicatorData(canvasWrapper, _groupBarsPosition!, holder);

if (data.extraLinesData.extraLinesOnTop) {
super.drawHorizontalLines(
context,
Expand Down Expand Up @@ -352,6 +355,75 @@ class BarChartPainter extends AxisChartPainter<BarChartData> {
}
}

@visibleForTesting
void drawErrorIndicatorData(
CanvasWrapper canvasWrapper,
List<GroupBarsPosition> groupBarsPosition,
PaintHolder<BarChartData> holder,
) {
final data = holder.data;
final errorIndicatorData = data.errorIndicatorData;
if (!errorIndicatorData.show) {
return;
}

final viewSize = canvasWrapper.size;
for (var i = 0; i < data.barGroups.length; i++) {
final barGroup = data.barGroups[i];
for (var j = 0; j < barGroup.barRods.length; j++) {
final barRod = barGroup.barRods[j];

if (barRod.toYErrorRange == null) {
continue;
}

final x = groupBarsPosition[i].barsX[j];

Check warning on line 380 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L380

Added line #L380 was not covered by tests

final y = getPixelY(barRod.toY, viewSize, holder);
final top = getPixelY(
barRod.toY + barRod.toYErrorRange!.upperBy,

Check warning on line 384 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L382-L384

Added lines #L382 - L384 were not covered by tests
viewSize,
holder,
) -

Check warning on line 387 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L387

Added line #L387 was not covered by tests
y;

final bottom = getPixelY(
barRod.toY - barRod.toYErrorRange!.lowerBy,

Check warning on line 391 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L390-L391

Added lines #L390 - L391 were not covered by tests
viewSize,
holder,
) -

Check warning on line 394 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L394

Added line #L394 was not covered by tests
y;

final relativeErrorPixelsRect = Rect.fromLTRB(

Check warning on line 397 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L397

Added line #L397 was not covered by tests
0,
top,
0,
bottom,
);

final painter = errorIndicatorData.painter(
BarChartSpotErrorRangeCallbackInput(

Check warning on line 405 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L404-L405

Added lines #L404 - L405 were not covered by tests
group: barGroup,
groupIndex: i,
rod: barRod,
barRodIndex: j,
),
);
canvasWrapper.drawErrorIndicator(

Check warning on line 412 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L412

Added line #L412 was not covered by tests
painter,
FlSpot(
barGroup.x.toDouble(),
barRod.toY,
yError: barRod.toYErrorRange,

Check warning on line 417 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L414-L417

Added lines #L414 - L417 were not covered by tests
),
Offset(x, y),

Check warning on line 419 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L419

Added line #L419 was not covered by tests
relativeErrorPixelsRect,
holder.data,

Check warning on line 421 in lib/src/chart/bar_chart/bar_chart_painter.dart

View check run for this annotation

Codecov / codecov/patch

lib/src/chart/bar_chart/bar_chart_painter.dart#L421

Added line #L421 was not covered by tests
);
}
}
}

@visibleForTesting
void drawTouchTooltip(
BuildContext context,
Expand Down Expand Up @@ -764,6 +836,7 @@ class BarChartPainter extends AxisChartPainter<BarChartData> {
@visibleForTesting
class GroupBarsPosition {
GroupBarsPosition(this.groupX, this.barsX);

final double groupX;
final List<double> barsX;
}

0 comments on commit f652121

Please sign in to comment.