Created
December 31, 2024 14:06
-
-
Save herveGuigoz/8b637b266ed15f17f50f3893f21ab819 to your computer and use it in GitHub Desktop.
Bar Chart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ignore_for_file: cascade_invocations | |
import 'dart:math' as math; | |
import 'package:flutter/widgets.dart'; | |
enum BarChartAlignment { start, center } | |
/// Constructs a bar chart widget. | |
class BarChartWidget extends LeafRenderObjectWidget { | |
const BarChartWidget({ | |
required this.entries, | |
this.alignment = BarChartAlignment.center, | |
this.barColor = const Color(0xFF2563EB), | |
this.dotColor = const Color(0xFFBFDBFE), | |
this.barWidth = 8.0, | |
this.barSpacing = 4.0, | |
this.labelInterval = 5, | |
this.labelStyle = const TextStyle(fontSize: 12, color: Color(0xFF6B7280)), | |
super.key, | |
}); | |
/// Represents the data values for each bar | |
final List<double> entries; | |
/// Determines the horizontal alignment of the bars in the chart. | |
final BarChartAlignment alignment; | |
/// Color of the bars in the chart. | |
final Color barColor; | |
/// Color of the dots for entries with a value of zero. | |
final Color dotColor; | |
/// The width of each bar in the chart. | |
final double barWidth; | |
/// The spacing between bars in the chart. | |
final double barSpacing; | |
/// The frequency at which labels are displayed. | |
final int labelInterval; | |
/// The style for the labels on X-axis. | |
final TextStyle labelStyle; | |
@override | |
RenderObject createRenderObject(BuildContext context) { | |
return BarChartRenderObject( | |
entries: entries, | |
alignment: alignment, | |
barColor: barColor, | |
dotColor: dotColor, | |
barWidth: barWidth, | |
barSpacing: barSpacing, | |
labelInterval: labelInterval, | |
labelStyle: labelStyle, | |
); | |
} | |
@override | |
void updateRenderObject(BuildContext context, covariant BarChartRenderObject renderObject) { | |
renderObject | |
..entries = entries | |
..alignment = alignment | |
..barColor = barColor | |
..dotColor = dotColor | |
..barWidth = barWidth | |
..barSpacing = barSpacing | |
..labelInterval = labelInterval | |
..labelStyle = labelStyle; | |
} | |
} | |
class BarChartRenderObject extends RenderBox { | |
BarChartRenderObject({ | |
required List<double> entries, | |
required BarChartAlignment alignment, | |
required Color barColor, | |
required Color dotColor, | |
required double barWidth, | |
required double barSpacing, | |
required int labelInterval, | |
required TextStyle labelStyle, | |
}) : _entries = entries, | |
_alignment = alignment, | |
_barColor = barColor, | |
_dotColor = dotColor, | |
_barWidth = barWidth, | |
_barSpacing = barSpacing, | |
_labelInterval = labelInterval, | |
_labelStyle = labelStyle; | |
List<double> _entries; | |
List<double> get entries => _entries; | |
set entries(List<double> value) { | |
if (_entries == value) { | |
return; | |
} | |
_entries = value; | |
markNeedsPaint(); | |
} | |
BarChartAlignment _alignment; | |
BarChartAlignment get alignment => _alignment; | |
set alignment(BarChartAlignment value) { | |
if (_alignment == value) { | |
return; | |
} | |
_alignment = value; | |
markNeedsPaint(); | |
} | |
Color _barColor; | |
Color get barColor => _barColor; | |
set barColor(Color value) { | |
if (_barColor == value) { | |
return; | |
} | |
_barColor = value; | |
markNeedsPaint(); | |
} | |
Color _dotColor; | |
Color get dotColor => _dotColor; | |
set dotColor(Color value) { | |
if (_dotColor == value) { | |
return; | |
} | |
_dotColor = value; | |
markNeedsPaint(); | |
} | |
double _barWidth; | |
double get barWidth => _barWidth; | |
set barWidth(double value) { | |
if (_barWidth == value) { | |
return; | |
} | |
_barWidth = value; | |
markNeedsPaint(); | |
} | |
double _barSpacing; | |
double get barSpacing => _barSpacing; | |
set barSpacing(double value) { | |
if (_barSpacing == value) { | |
return; | |
} | |
_barSpacing = value; | |
markNeedsPaint(); | |
} | |
int _labelInterval; | |
int get labelInterval => _labelInterval; | |
set labelInterval(int value) { | |
if (_labelInterval == value) return; | |
_labelInterval = value; | |
markNeedsPaint(); | |
} | |
TextStyle _labelStyle; | |
TextStyle get labelStyle => _labelStyle; | |
set labelStyle(TextStyle value) { | |
if (_labelStyle == value) { | |
return; | |
} | |
_labelStyle = value; | |
markNeedsPaint(); | |
} | |
@override | |
void performLayout() { | |
size = constraints.constrain(constraints.biggest); | |
} | |
@override | |
void paint(PaintingContext context, Offset offset) { | |
final canvas = context.canvas; | |
// Chart dimensions | |
final spacing = barSpacing * (entries.length - 1); | |
final effectiveBarWidth = math.min(barWidth, (size.width - spacing) / entries.length); | |
final chartHeight = size.height - 20; // Leave space for labels | |
final chartWidth = effectiveBarWidth * entries.length + spacing; | |
// Adjust the starting offset | |
final xOffset = switch (alignment) { | |
BarChartAlignment.start => 0, | |
BarChartAlignment.center => (size.width - chartWidth) / 2, | |
}; | |
// Bar scaling | |
final maxY = entries.isNotEmpty ? entries.reduce((a, b) => a > b ? a : b) : 10; | |
final scale = chartHeight / maxY; | |
final paint = Paint() | |
..color = barColor | |
..style = PaintingStyle.fill; | |
final dotPaint = Paint() | |
..color = dotColor | |
..style = PaintingStyle.fill; | |
// Draw bars or dots | |
for (var i = 0; i < entries.length; i++) { | |
final x = offset.dx + xOffset + i * (effectiveBarWidth + barSpacing); | |
final value = entries[i]; | |
if (value == 0) { | |
// Draw a dot for zero value | |
final dotY = offset.dy + chartHeight - value * scale; | |
canvas.drawCircle( | |
Offset(x + effectiveBarWidth / 2, dotY - effectiveBarWidth / 2), | |
effectiveBarWidth / 2, | |
dotPaint, | |
); | |
} else { | |
// Draw a bar for non-zero value | |
final barRect = Rect.fromLTWH( | |
x, | |
offset.dy + chartHeight - value * scale, | |
effectiveBarWidth, | |
value * scale, | |
); | |
final rRect = RRect.fromRectAndRadius(barRect, const Radius.circular(8)); | |
canvas.drawRRect(rRect, paint); | |
} | |
} | |
// Draw X-axis labels every 5 entries | |
final textPainter = TextPainter( | |
textAlign: TextAlign.center, | |
textDirection: TextDirection.ltr, | |
); | |
for (var i = 0; i < entries.length; i++) { | |
if (i == 0 || (i + 1) % labelInterval == 0) { | |
textPainter.text = TextSpan( | |
text: (i + 1).toString(), | |
style: labelStyle, | |
); | |
textPainter.layout(); | |
textPainter.paint( | |
canvas, | |
Offset( | |
offset.dx + xOffset + i * (effectiveBarWidth + barSpacing) + effectiveBarWidth / 2 - textPainter.width / 2, | |
offset.dy + chartHeight + 5, | |
), | |
); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment