Skip to content

Instantly share code, notes, and snippets.

@herveGuigoz
Created December 31, 2024 14:06
Show Gist options
  • Save herveGuigoz/8b637b266ed15f17f50f3893f21ab819 to your computer and use it in GitHub Desktop.
Save herveGuigoz/8b637b266ed15f17f50f3893f21ab819 to your computer and use it in GitHub Desktop.
Bar Chart
// 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