import 'package:flutter/material.dart'; import 'package:fl_chart/fl_chart.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'; /// /// CustomGraph(dataPoints: sampleData, scrollDirection: Axis.horizontal,height: 200,maxY: 100, maxX:2.5, /// leftLabelFormatter: (value){ /// Widget buildLabel(String label) { /// return Padding( /// padding: const EdgeInsets.only(right: 8), /// child: Text( /// label, /// style: TextStyle( /// fontSize: 8.fSize, color: AppColors.textColor, /// fontFamily: /// FontUtils.getFontFamilyForLanguage(false) /// ), /// textAlign: TextAlign.right, /// ), /// ); /// } /// switch (value.toInt()) { /// /// case 20: /// return buildLabel("Critical Low"); /// case 40: /// return buildLabel("Low"); /// case 60: /// return buildLabel("Normal"); /// case 80: /// return buildLabel("High"); /// case 100: /// return buildLabel("Critical High"); /// } /// return const SizedBox.shrink(); /// }, /// /// ), class CustomGraph extends StatelessWidget { final List dataPoints; final double? width; final double height; final double? maxY; final double? maxX; final double? minX; final Color spotColor; final Color graphColor; final Color graphShadowColor; final Color graphGridColor; final Color bottomLabelColor; final double? bottomLabelSize; final FontWeight? bottomLabelFontWeight; ///creates the left label and provide it to the chart as it will be used by other part of the application so the label will be different for every chart final Widget Function(double) leftLabelFormatter; final Widget Function(double , List) bottomLabelFormatter; final Axis scrollDirection; final bool showBottomTitleDates; final bool isFullScreeGraph; const CustomGraph({ super.key, required this.dataPoints, required this.leftLabelFormatter, this.width, required this.scrollDirection, required this.height, this.maxY, this.maxX, this.showBottomTitleDates = true, this.isFullScreeGraph = false, this.spotColor = AppColors.bgGreenColor, this.graphColor = AppColors.bgGreenColor, this.graphShadowColor = AppColors.graphGridColor, this.graphGridColor = AppColors.graphGridColor, this.bottomLabelColor = AppColors.textColor, this.bottomLabelFontWeight = FontWeight.w500, this.bottomLabelSize, required this.bottomLabelFormatter, this.minX, }); @override Widget build(BuildContext context) { // var maxY = 0.0; double interval = 20; if ((maxY ?? 0) > 10 && (maxY ?? 0) <= 20) { interval = 2; } else if ((maxY ?? 0) > 5 && (maxY ?? 0) <= 10) { interval = 1; } else if ((maxY ?? 0) >= 0 && (maxY ?? 0) <= 5) { interval = .4; } return Material( color: Colors.white, child: SizedBox( width: width, height: height, child: LineChart( LineChartData( minY: 0, maxY: ((maxY?.ceilToDouble() ?? 0.0) + interval).floorToDouble(), // minX: dataPoints.first.labelValue - 1, maxX: maxX, minX: minX ??-0.2, lineTouchData: LineTouchData( getTouchLineEnd: (_, __) => 0, getTouchedSpotIndicator: (barData, indicators) { // Only show custom marker for touched spot return indicators.map((int index) { return TouchedSpotIndicatorData( FlLine(color: Colors.transparent), FlDotData( show: true, getDotPainter: (spot, percent, barData, idx) { return FlDotCirclePainter( radius: 8, color: spotColor, strokeWidth: 2, strokeColor: Colors.white, ); }, ), ); }).toList(); }, enabled: true, touchTooltipData: LineTouchTooltipData( getTooltipColor: (_) => Colors.white, getTooltipItems: (touchedSpots) { if (touchedSpots.isEmpty) return []; // Only show tooltip for the first touched spot, hide others return touchedSpots.map((spot) { if (spot == touchedSpots.first) { final dataPoint = dataPoints[spot.x.toInt()]; return LineTooltipItem( // '${dataPoint.label} ${spot.y.toStringAsFixed(2)}', '${dataPoint.actualValue} ${dataPoint.displayTime}', TextStyle( color: Colors.black, fontSize: 12.fSize, fontWeight: FontWeight.w500), ); } return null; // hides the rest }).toList(); }, ), ), titlesData: FlTitlesData( leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, reservedSize: 77, interval: .1, // Let fl_chart handle it getTitlesWidget: (value, _) { return leftLabelFormatter(value); }, ), ), bottomTitles: AxisTitles( axisNameSize: 20, sideTitles: SideTitles( showTitles: showBottomTitleDates, reservedSize: 20, getTitlesWidget: (value, _) { return bottomLabelFormatter(value, dataPoints, ); }, interval: 1, // ensures 1:1 mapping with spots ), ), 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, ), ), lineBarsData: _buildColoredLineSegments(dataPoints), gridData: FlGridData( show: true, drawVerticalLine: false, horizontalInterval: 20, checkToShowHorizontalLine: (value) => value >= 0 && value <= 100, getDrawingHorizontalLine: (value) { return FlLine( color: AppColors.graphGridColor, strokeWidth: 1, dashArray: [5, 5], ); }, ), ), ), )); } List _buildColoredLineSegments(List dataPoints) { final List allSpots = dataPoints.asMap().entries.map((entry) { return FlSpot(entry.key.toDouble(), entry.value.value); }).toList(); var data = [ LineChartBarData( spots: allSpots, isCurved: true, isStrokeCapRound: true, isStrokeJoinRound: true, barWidth: 4, gradient: LinearGradient( colors: [graphColor, graphColor], begin: Alignment.centerLeft, end: Alignment.centerRight, ), dotData: FlDotData( show: false, ), belowBarData: BarAreaData( show: true, gradient: LinearGradient( colors: [ graphShadowColor, Colors.transparent, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ) ]; return data; } // Widget buildLabel(String label) { // return Padding( // padding: const EdgeInsets.only(right: 8), // child: Text( // label, // style: TextStyle( // fontSize: leftLabelSize ?? 8.fSize, color: leftLabelColor), // textAlign: TextAlign.right, // ), // ); // } } // final List sampleData = [ // DataPoint( // value: 20, // label: 'Jan 2024', // ), // DataPoint( // value: 36, // label: 'Feb 2024', // ), // DataPoint( // value: 80, // label: 'This result', // ), // ];