You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HMG_Patient_App_New/lib/widgets/graph/CustomBarGraph.dart

250 lines
8.8 KiB
Dart

import 'package:easy_localization/easy_localization.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/data_points.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
/// A customizable bar chart widget using `fl_chart`.
///
/// Displays a bar chart with configurable axis labels, colors, and data points.
/// Useful for visualizing comparative data, categories, or grouped values.
///
/// **Parameters:**
/// - [dataPoints]: List of `DataPoint` objects to plot.
/// - [secondaryDataPoints]: Optional list for grouped bars (e.g., comparison data).
/// - [leftLabelFormatter]: Function to build left axis labels.
/// - [bottomLabelFormatter]: Function to build bottom axis labels.
/// - [width]: Optional width of the chart.
/// - [height]: Required height of the chart.
/// - [maxY], [maxX], [minX]: Axis bounds.
/// - [barColor]: Color of the bars.
/// - [secondaryBarColor]: Color of the secondary bars.
/// - [barRadius]: Border radius for bar corners.
/// - [barWidth]: Width of each bar.
/// - [bottomLabelColor]: Color of bottom axis labels.
/// - [bottomLabelSize]: Font size for bottom axis labels.
/// - [bottomLabelFontWeight]: Font weight for bottom axis labels.
/// - [leftLabelInterval]: Interval between left axis labels.
/// - [leftLabelReservedSize]: Reserved space for left axis labels.
/// - [showBottomTitleDates]: Whether to show bottom axis labels.
/// - [isFullScreeGraph]: Whether the graph is fullscreen.
/// - [makeGraphBasedOnActualValue]: Use `actualValue` for plotting.
///
/// Example usage:
/// ```dart
/// CustomBarChart(
/// dataPoints: sampleData,
/// leftLabelFormatter: (value) => ...,
/// bottomLabelFormatter: (value, dataPoints) => ...,
/// height: 300,
/// maxY: 100,
/// )
class CustomBarChart extends StatelessWidget {
final List<DataPoint> dataPoints;
final List<DataPoint>? secondaryDataPoints; // For grouped bar charts
final double? width;
final double height;
final double? maxY;
final double? maxX;
final double? minX;
Color? barColor;
final Color? secondaryBarColor;
Color? barGridColor;
Color? bottomLabelColor;
final double? bottomLabelSize;
final FontWeight? bottomLabelFontWeight;
final double? leftLabelInterval;
final double? leftLabelReservedSize;
final double? bottomLabelReservedSize;
final bool? showGridLines;
final GetDrawingGridLine? getDrawingVerticalLine;
final double? verticalInterval;
final double? minY;
final BorderRadius? barRadius;
final double barWidth;
final BarTooltipItem Function(DataPoint)? getTooltipItem;
/// Creates the left label and provides it to the chart
final Widget Function(double) leftLabelFormatter;
final Widget Function(double, List<DataPoint>) bottomLabelFormatter;
final bool showBottomTitleDates;
final bool isFullScreeGraph;
final bool makeGraphBasedOnActualValue;
CustomBarChart(
{super.key,
required this.dataPoints,
this.secondaryDataPoints,
required this.leftLabelFormatter,
this.width,
required this.height,
this.maxY,
this.maxX,
this.showBottomTitleDates = true,
this.isFullScreeGraph = false,
this.secondaryBarColor,
this.bottomLabelFontWeight = FontWeight.w500,
this.bottomLabelSize,
this.leftLabelInterval,
this.leftLabelReservedSize,
this.bottomLabelReservedSize,
this.makeGraphBasedOnActualValue = false,
required this.bottomLabelFormatter,
this.minX,
this.showGridLines = false,
this.getDrawingVerticalLine,
this.verticalInterval,
this.minY,
this.barRadius,
this.barWidth = 16,
this.getTooltipItem,
this.barColor ,
this.barGridColor ,
this.bottomLabelColor,
});
@override
Widget build(BuildContext context) {
barColor ??= AppColors.bgGreenColor;
barGridColor ??= AppColors.graphGridColor;
bottomLabelColor ??= AppColors.textColor;
return Material(
color: Colors.white,
child: SizedBox(
width: width,
height: height,
child: BarChart(
BarChartData(
minY: minY ?? 0,
maxY: maxY,
barTouchData: BarTouchData(
handleBuiltInTouches: true,
touchCallback: (FlTouchEvent event, BarTouchResponse? touchResponse) {
// Let fl_chart handle the touch
},
touchTooltipData: BarTouchTooltipData(
getTooltipColor: (_)=>AppColorsContext.tooltipColor,
getTooltipItem: (group, groupIndex, rod, rodIndex) {
final dataPoint = dataPoints[groupIndex];
if(getTooltipItem != null) {
return getTooltipItem!(dataPoint);
}
return BarTooltipItem(
'${dataPoint.actualValue} ${dataPoint.unitOfMeasurement ?? ""}\n${DateFormat('dd MMM, yyyy').format(dataPoint.time)}',
TextStyle(
color: Colors.black,
fontSize: 12.f,
fontWeight: FontWeight.w500,
),
);
},
),
enabled: true,
),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: leftLabelReservedSize ?? 80,
interval: leftLabelInterval ?? .1,
getTitlesWidget: (value, _) {
return leftLabelFormatter(value);
},
),
),
bottomTitles: AxisTitles(
axisNameSize: 20,
sideTitles: SideTitles(
showTitles: showBottomTitleDates,
reservedSize: bottomLabelReservedSize ?? 30,
getTitlesWidget: (value, _) {
return bottomLabelFormatter(value, dataPoints);
},
interval: 1,
),
),
topTitles: AxisTitles(),
rightTitles: AxisTitles(),
),
borderData: FlBorderData(
show: true,
border: const Border(
bottom: BorderSide.none,
left: BorderSide(color: Colors.grey, width: .5),
right: BorderSide.none,
top: BorderSide.none,
),
),
barGroups: _buildBarGroups(dataPoints),
gridData: FlGridData(
show: showGridLines ?? true,
drawHorizontalLine: false,
verticalInterval: verticalInterval,
getDrawingVerticalLine: getDrawingVerticalLine ??
(value) {
return FlLine(
color: barGridColor,
strokeWidth: 1,
dashArray: [5, 5],
);
},
)),
),
),
);
}
/// Builds bar chart groups from data points
List<BarChartGroupData> _buildBarGroups(List<DataPoint> dataPoints) {
return dataPoints.asMap().entries.map((entry) {
final index = entry.key;
final dataPoint = entry.value;
double value = (makeGraphBasedOnActualValue)
? double.tryParse(dataPoint.actualValue) ?? 0.0
: dataPoint.value;
final barRods = <BarChartRodData>[
BarChartRodData(
toY: value,
color: barColor,
width: barWidth,
borderRadius: barRadius ?? BorderRadius.circular(6),
// backDrawRodData: BackgroundBarChartRodData(
// show: true,
// toY: maxY,
// color: Colors.grey[100],
// ),
),
];
// Add secondary bar if provided (for grouped bar charts)
if (secondaryDataPoints != null && index < secondaryDataPoints!.length) {
final secondaryDataPoint = secondaryDataPoints![index];
double secondaryValue = (makeGraphBasedOnActualValue)
? double.tryParse(secondaryDataPoint.actualValue) ?? 0.0
: secondaryDataPoint.value;
barRods.add(
BarChartRodData(
toY: secondaryValue,
color: secondaryBarColor ?? AppColors.blueColor,
width: barWidth,
borderRadius: barRadius ?? BorderRadius.circular(6),
),
);
}
return BarChartGroupData(
x: index,
barRods: barRods,
barsSpace: 8.w
);
}).toList();
}
}