nhance_partner/lib/presentation/themes/charts/barChart_horizontal.dart
venbaittech 10e5751367 bug fix
2025-12-24 12:48:16 +05:30

403 lines
15 KiB
Dart

import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import '../../../core/services/api_service.dart';
import '../../../data/models/bar_data.dart';
import '../../providers/manager_provider.dart';
import 'appChartColors.dart';
class CustomBarChartHorizontal extends ConsumerStatefulWidget {
final List<BarData> dataList;
final List<String> labels;
final List<String>? shortName;
final String title;
final String? btnName;
final double maxY;
final int initialRotation;
const CustomBarChartHorizontal({
super.key,
required this.dataList,
required this.labels,
this.shortName,
this.btnName,
this.title = "Horizontal Bar Chart",
this.maxY = 20,
this.initialRotation = 1,
});
final shadowColor = const Color(0xFFCCCCCC);
@override
// State<CustomBarChartHorizontal> createState() => _CustomBarChartState();
ConsumerState<CustomBarChartHorizontal> createState() =>
_CustomBarChartState();
}
class _CustomBarChartState extends ConsumerState<CustomBarChartHorizontal> {
int touchedGroupIndex = -1;
late int rotationTurns;
dynamic managerId;
late ApiService apiService;
@override
void initState() {
super.initState();
apiService = ApiService();
rotationTurns = widget.initialRotation;
Future.microtask(() {
managerId = ref.read(managerIdProvider);
});
}
final indianFormatter = NumberFormat('#,##,##0', 'en_IN');
BarChartGroupData generateBarGroup(
int x,
Color color,
double value1,
double value2,
double shadowValue,
) {
return BarChartGroupData(
x: x,
groupVertically: false,
// showingTooltipIndicators: [x],
showingTooltipIndicators: const [1, 2],
// barsSpace: 30,
barRods: [
BarChartRodData(toY: 0, color: Colors.transparent, width: 10),
BarChartRodData(
toY: value1,
color: const Color(0xFF34a9a8),
// color: Colors.cyan.shade300,
width: 10,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(5),
),
// borderRadius: BorderRadius.circular(5),
),
// BarChartRodData(toY: 2, color: Colors.orange.shade300, width: 6),
BarChartRodData(
toY: value2,
// toY: value2,
color: const Color(0xFFA5E4E1),
// color: Colors.orange.shade300,
width: 10,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(5),
topRight: Radius.circular(5),
),
// borderRadius: BorderRadius.circular(5),
),
],
// showingTooltipIndicators: touchedGroupIndex == x ? [0] : [],
);
}
String compactNumber(num value) {
if (value >= 10000000) {
return '${(value / 10000000).toStringAsFixed(1).replaceAll('.0', '')}Cr';
} else if (value >= 1000000) {
return '${(value / 1000000).toStringAsFixed(1).replaceAll('.0', '')}M';
} else if (value >= 1000) {
return '${(value / 1000).toStringAsFixed(1).replaceAll('.0', '')}K';
} else {
return value.toInt().toString();
}
}
// Widget build(BuildContext context) {
// return AspectRatio(
// aspectRatio: 1.4,
//
// );
// }
double getChartWidth() {
const double barWidth = 40; // bar + spacing
double minWidth =
MediaQuery.of(context).size.width * 0.9; // minimum chart width
final calculatedWidth = widget.dataList.length * barWidth;
return calculatedWidth < minWidth ? minWidth : calculatedWidth;
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
return SizedBox(
// width: constraints.maxWidth,
// height: constraints.maxHeight,
width: getChartWidth(), // 👈 IMPORTANT
height: constraints.maxHeight,
child: BarChart(
BarChartData(
// alignment: BarChartAlignment.spaceBetween,
alignment: BarChartAlignment.start,
// verticalAxis: Axis.horizontal,
rotationQuarterTurns: rotationTurns,
barTouchData: BarTouchData(
enabled: true,
handleBuiltInTouches: true,
touchTooltipData: BarTouchTooltipData(
tooltipMargin: 6,
tooltipRoundedRadius: 6,
getTooltipColor: (group) {
return Colors.transparent; // 👈 tooltip background color
},
tooltipPadding: const EdgeInsets.symmetric(
horizontal: 7,
vertical: 2,
),
// rotateAngle: -85,
// ask fl_chart to fit tooltip inside available space
fitInsideVertically:
true, // if your fl_chart version supports it
fitInsideHorizontally:
true, // if your fl_chart version supports it
getTooltipItem:
(
BarChartGroupData group,
int groupIndex,
BarChartRodData rod,
int rodIndex,
) {
if (rod.toY == 0) {
return null;
}
// final textColor = rodIndex == 1
// ? Colors.cyan.shade500
// : Colors.orange.shade500;
// final textColor = Colors.black;
final textColor = rodIndex == 1
? Color(0xFF34a9a8)
: Color(0xFF020050);
final textStyle = GoogleFonts.poppins(
color: textColor,
fontSize: 8,
fontWeight: rodIndex == 1
? FontWeight.w600
: FontWeight.w400,
);
// final formattedValue = indianFormatter.format(
// rod.toY.round(),
// );
final formattedValue = compactNumber(rod.toY);
return BarTooltipItem(formattedValue, textStyle);
// return BarTooltipItem('${rod.toY.round()}', textStyle);
},
),
touchCallback: (FlTouchEvent e, BarTouchResponse? r) async {
// optional: change state to highlight tapped group
if (r == null || r.spot == null) {
setState(() => touchedGroupIndex = -1);
return;
}
setState(
() => touchedGroupIndex = r.spot!.touchedBarGroupIndex,
);
final spot = r.spot!;
final groupIndex = spot.touchedBarGroupIndex;
final rodIndex = spot.touchedRodDataIndex;
final barData = widget.dataList[groupIndex];
final name = barData.chartName;
final val = barData.id;
final month = rodIndex == 1 ? 'current' : 'previous';
print('name - $name');
print('val - $val');
print('month - $month');
print('managerId - $managerId');
if (e is FlTapUpEvent) {
debugPrint(
'Tapped value → ${rodIndex == 1 ? barData.value1 : barData.value2} -> ${barData.id}-'
' ${rodIndex == 1 ? 'current' : 'previous'} -> ${barData.chartName}',
);
if (managerId != null) {
print('managerId - $managerId');
await apiService.generateChartExcel(
name,
val,
month,
managerId,
);
}
}
},
),
borderData: FlBorderData(
show: true,
border: Border(
bottom: BorderSide(
color: AppColors.gridLinesColor,
// color: AppColors.contentColorBlack,
width: 0.1,
),
right: BorderSide(
color: AppColors.gridLinesColor,
// color: AppColors.contentColorBlack,
width: 0.1,
),
),
),
gridData: FlGridData(
show: true,
// drawVerticalLine: true,
drawVerticalLine: false,
getDrawingHorizontalLine: (value) => FlLine(
// color: AppColors.gridLinesColor.withValues(alpha: 0.2),
color: Colors.blueGrey.shade100,
// color: Colors.black,
// color: AppColors.borderColor,
strokeWidth: 0.3,
),
),
titlesData: FlTitlesData(
bottomTitles: AxisTitles(
axisNameWidget: widget.btnName != null
? Text(widget.btnName!)
: Text(''),
axisNameSize: 10,
sideTitles: SideTitles(
// reservedSize: 90,
reservedSize: MediaQuery.of(context).size.width * 0.1,
showTitles: true,
getTitlesWidget: (value, meta) {
final i = value.toInt();
if (i >= widget.labels.length) return const SizedBox();
final hasShortName =
widget.shortName != null &&
widget.shortName!.length > i &&
widget.shortName![i].trim().isNotEmpty;
return SideTitleWidget(
meta: meta,
child: hasShortName
? Tooltip(
message: widget.shortName![i],
child: Transform.rotate(
angle: 0,
child: Text(
// widget.labels[i],
truncate(widget.labels[i], max: 5),
style: GoogleFonts.poppins(
fontSize: 9,
fontWeight: FontWeight.w600,
color: Color(0xFF1E293B),
),
textAlign: TextAlign.end,
),
),
)
: Transform.rotate(
angle: 0,
child: Text(
widget.labels[i],
style: GoogleFonts.poppins(
fontSize: 9,
fontWeight: FontWeight.w600,
color: Color(0xFF1E293B),
),
textAlign: TextAlign.end,
),
),
);
},
),
),
rightTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
// interval: 10,
getTitlesWidget: (value, meta) {
// print("valuevalue - $value");
final text = value.toInt().toString();
final bool isLast = value == meta.max;
// if (isLast) {
// return const SizedBox.shrink(); // 👈 hide last label
// }
// print('value: $value');
// print('text: $text');
// print('length: ${text.length}');
// print('isLast: $isLast');
return Transform.rotate(
angle: -3,
// angle: 0,
child: Padding(
padding: const EdgeInsets.only(right: 3.0),
child: Text(
// isLast ? '0' : value.toInt().toString(),
isLast ? '0' : compactNumber(value),
textAlign: TextAlign.end,
style: GoogleFonts.inter(
fontSize: 9,
// color: isLast ? Colors.white : Colors.black,
color: Colors.white,
fontWeight: isLast
? FontWeight.w100
: FontWeight.w400,
),
),
),
);
},
reservedSize: 5,
),
),
leftTitles: const AxisTitles(),
topTitles: const AxisTitles(),
),
barGroups: widget.dataList.asMap().entries.map((e) {
final index = e.key;
final data = e.value;
print('generateBarGroupBAR - $data');
return generateBarGroup(
index,
data.color,
data.value1,
data.value2,
data.shadowValue,
);
}).toList(),
groupsSpace: 12,
// maxY: widget.maxY,
maxY: (widget.dataList != null && widget.dataList!.isNotEmpty)
? widget.dataList!
.map(
(e) => e.value1 > e.value2 ? e.value1 : e.value2,
)
.reduce((a, b) => a > b ? a : b) *
1.15
: 20,
),
),
);
},
);
}
String truncate(String text, {int max = 6}) {
if (text.length <= max) return text;
return text.substring(0, max);
}
}