bhavin feedback changes

This commit is contained in:
Surendiran 2025-06-12 11:45:26 +05:30
parent ebf5b5e853
commit f6c14eb2e5
4 changed files with 615 additions and 183 deletions

View File

@ -1462,6 +1462,10 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
void applyFilters(BuildContext context, List filters, List data, void applyFilters(BuildContext context, List filters, List data,
List dataCard, List selectedFilters) { List dataCard, List selectedFilters) {
setState(() {
isLoading = true;
});
Navigator.pop(context);
print('applyFilters called'); print('applyFilters called');
print('Selected ApplyFilters: $selectedFilters'); print('Selected ApplyFilters: $selectedFilters');
@ -1505,7 +1509,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
tabWiseKpi, // Ensure you are passing the correct KPI here tabWiseKpi, // Ensure you are passing the correct KPI here
formattedFilters // Pass the formatted filter data here formattedFilters // Pass the formatted filter data here
); );
Navigator.pop(context); // Navigator.pop(context);
selectedFiltersStorage = List.from(selectedFilters); selectedFiltersStorage = List.from(selectedFilters);
print('selectedFiltersStoraged- $selectedFiltersStorage'); print('selectedFiltersStoraged- $selectedFiltersStorage');
@ -1999,7 +2003,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
Navigator.pop(context); Navigator.pop(context);
} else { } else {
setState(() { setState(() {
isLoading = true; // isLoading = true;
chartsData = originalTabChartsData; chartsData = originalTabChartsData;
cardData = originalTabCardData; cardData = originalTabCardData;
}); });

View File

@ -90,20 +90,28 @@ class ChartWidget extends StatelessWidget {
// Function to get colors for both English & Arabic keys // Function to get colors for both English & Arabic keys
List<Color> get uniqueColors { List<Color> get uniqueColors {
final Map<String, String> translations = { final Map<String, String> translations = {
'الاقتصاد': 'ECONOMY', 'اقتصاد': 'ECONOMY',
'الاجتماعي': 'SOCIAL', 'اجتماعي': 'SOCIAL',
'البيئة': 'ENVIRONMENT', 'بيئة': 'ENVIRONMENT',
}; };
print('color check');
print(translations);
print('chartHeader $chartHeader');
print(translations[chartHeader]);
String key = translations[chartHeader] ?? chartHeader; String key = translations[chartHeader] ?? chartHeader;
print(key);
print(colorMap[key]);
return colorMap[key] ?? [Colors.grey]; return colorMap[key] ?? [Colors.grey];
} }
List<Color> get uniqueColorsForTwo { List<Color> get uniqueColorsForTwo {
final Map<String, String> translations = { final Map<String, String> translations = {
'الاقتصاد': 'ECONOMY', 'اقتصاد': 'ECONOMY',
'الاجتماعي': 'SOCIAL', 'اجتماعي': 'SOCIAL',
'البيئة': 'ENVIRONMENT', 'بيئة': 'ENVIRONMENT',
}; };
String key = translations[chartHeader] ?? chartHeader; String key = translations[chartHeader] ?? chartHeader;
@ -112,9 +120,9 @@ class ChartWidget extends StatelessWidget {
List<Color> get uniqueColorsForThree { List<Color> get uniqueColorsForThree {
final Map<String, String> translations = { final Map<String, String> translations = {
'الاقتصاد': 'ECONOMY', 'اقتصاد': 'ECONOMY',
'الاجتماعي': 'SOCIAL', 'اجتماعي': 'SOCIAL',
'البيئة': 'ENVIRONMENT', 'بيئة': 'ENVIRONMENT',
}; };
String key = translations[chartHeader] ?? chartHeader; String key = translations[chartHeader] ?? chartHeader;
@ -1271,12 +1279,7 @@ class ChartWidget extends StatelessWidget {
Set<int> selectedYears = {1970, 1980, 1990, 2000, 2010, 2020}; Set<int> selectedYears = {1970, 1980, 1990, 2000, 2010, 2020};
Map<String, Color> groupColorMap = {}; Map<String, Color> groupColorMap = {};
int colorIndex = 0; int colorIndex = 0;
// for (String group in groupByValues) {
// // groupColorMap[group] = uniqueColors[colorIndex % uniqueColors.length];
// groupColorMap[group] = uniqueColorsLine_trend_2[
// colorIndex % uniqueColorsLine_trend_2.length];
// colorIndex++;
// }
// Extract all years from the chart data // Extract all years from the chart data
List<int> years = (chartData['response'] as List<dynamic>) List<int> years = (chartData['response'] as List<dynamic>)
@ -1322,16 +1325,7 @@ class ChartWidget extends StatelessWidget {
barColorsMap[key] = value.toString(); // Ensure values are strings barColorsMap[key] = value.toString(); // Ensure values are strings
}); });
bool hasBarColors = barColorsMap.isNotEmpty; bool hasBarColors = barColorsMap.isNotEmpty;
// var chartBarColorsRaw = chartData['chart_bar_color'] ?? {};
// // Convert LinkedMap<dynamic, dynamic> to Map<String, String>
// Map<String, String> chartBarColrs =
// Map<String, String>.from(chartBarColorsRaw);
// // Now map to Color
// Map<String, Color> parsedChartBarColors =
// chartBarColrs.map((key, value) {
// return MapEntry(key, _hexToColor(value));
// });
//
List<dynamic> keys = chartBarColors['key'] ?? []; List<dynamic> keys = chartBarColors['key'] ?? [];
List<dynamic> colors = chartBarColors['color'] ?? []; List<dynamic> colors = chartBarColors['color'] ?? [];
@ -1359,109 +1353,288 @@ class ChartWidget extends StatelessWidget {
colorIndex++; colorIndex++;
} }
// Generate line bars for the chart
List<LineChartBarData> lineBars = lineBarsData(
filteredData,
groupByValues,
groupByKey,
parsedChartBarColors,
);
return Column(
children: [
Text(
chartData['chart_heading'] ?? '',
style: TextStyle(fontFamily: context.translate(
'Roboto',
'NotoKufi',
),fontSize: 14, fontWeight: FontWeight.w400),
textAlign: TextAlign.center,
),
SizedBox(height: 5),
Text(
chartData['chart_sub_heading'] ?? '',
style: TextStyle(fontFamily: context.translate(
'Roboto',
'NotoKufi',
),fontSize: 13, fontWeight: FontWeight.w300),
textAlign: TextAlign.center,
),
SizedBox(height: 10),
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal, // Enable horizontal scrolling final Map<String, List<Map<String, dynamic>>> groupedData = {};
padding: const EdgeInsets.only(right: 40, top: 10),
child: SizedBox( // Step 1: Group data by PLANT_TYPE
width: (uniqueXValues.length * 50) + for (var item in filteredData) {
50, // Adjust width dynamically final plantType = item['ObsKey'][groupByKey];
child: LineChart( groupedData.putIfAbsent(plantType, () => []).add(item);
LineChartData( }
lineTouchData: lineTouchData1(context),
gridData: gridData(), // Step 2: Convert grouped data into List<LineChartBarData>
titlesData: titlesData1(uniqueXValues,context), final List<LineChartBarData> lineBars = [];
borderData: borderData(), final List<Color> colorPalette = [
lineBarsData: lineBars, Color(0xFF6097CD),
minX: uniqueXValues.reduce((a, b) => a < b ? a : b), Color(0xFFD086A7),
maxX: uniqueXValues.reduce((a, b) => a > b ? a : b), Color(0xFF98BCE5),
), Color(0xFFA7B5C5),
), Color(0xFFBED3EC),
Color(0xFFD4E3F4),
];
String formatNumber(double value) {
if (value >= 1e12) {
return '${(value / 1e12).toStringAsFixed(2)}T';
} else if (value >= 1e9) {
return '${(value / 1e9).toStringAsFixed(2)}B';
} else if (value >= 1e6) {
return '${(value / 1e6).toStringAsFixed(2)}M';
} else if (value >= 1e3) {
return '${(value / 1e3).toStringAsFixed(2)}K';
} else {
return value.toStringAsFixed(2); // No decimals for small numbers
}
}
final getXValue = chartData['chart_type_json']['x'] ?? '';
groupedData.forEach((plantType, entries) {
final spots = entries.map((entry) {
final year = double.tryParse(entry['ObsKey'][getXValue].toString()) ?? 0;
final value = double.tryParse(entry['ObsValue']['Value'].toString()) ?? 0;
return FlSpot(year, value);
}).toList()
..sort((a, b) => a.x.compareTo(b.x)); // Sort by year
final baseColor = groupColorMap[plantType] ?? Colors.grey;
lineBars.add(
LineChartBarData(
spots: spots,
isCurved: true,
barWidth: 3,
// color: groupColorMap[plantType] ?? Colors.grey,
color: baseColor.withOpacity(0.9),
dotData: FlDotData(show: true),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: [
// baseColor.withOpacity(0.2), // visible base color
// Colors.white, // fade to white
baseColor.withOpacity(0.4), // increase opacity
baseColor.withOpacity(0.0), // fade to transparent (not white)
],
stops: [
0.2, // shift teal color start downward (20% from the top)
1.0, // white at the bottom
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
), ),
), ),
showingIndicators: List.generate(spots.length, (index) => index),
), ),
Padding( );
padding: const EdgeInsets.all(8.0), colorIndex++;
child: Wrap( });
spacing: 12,
runSpacing: 8, return Scaffold(
children: groupByValues.map((group) { backgroundColor: Colors.white,
return Row( body: Padding(
mainAxisSize: MainAxisSize.min, padding: const EdgeInsets.all(10),
children: [ child: LayoutBuilder(
Container( builder: (context, constraints) {
width: 12, final chartWidth = constraints.maxWidth;
height: 12, final chartHeight = constraints.maxHeight;
color: groupColorMap[group], final allSpots = lineBars.expand((line) => line.spots);
final minY = allSpots.map((e) => e.y).reduce((a, b) => a < b ? a : b);
final maxY = allSpots.map((e) => e.y).reduce((a, b) => a > b ? a : b);
final xValues = allSpots.map((e) => e.x).toList();
final minX = xValues.reduce((a, b) => a < b ? a : b).toInt();
final maxX = xValues.reduce((a, b) => a > b ? a : b).toInt();
final paddingFactor = 0.3; // 10% extra padding
final chartMinY = minY - (maxY - minY) * paddingFactor;
final chartMaxY = maxY + (maxY - minY) * paddingFactor;
return Column(
crossAxisAlignment: CrossAxisAlignment.center, // ensures center alignment
children: [
Padding(
padding: const EdgeInsets.only(top: 0),
child: Text(
chartData['chart_heading'] ?? 'Production Quantity by Crop Type (Metric Tonne)',
style: TextStyle(
fontFamily: context.translate('Roboto', 'NotoKufi'),
fontSize: 14,
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.center,
), ),
SizedBox(width: 6), ),
Text(group, style: TextStyle(fontFamily: context.translate( // SizedBox(height: 6),
'Roboto', Expanded(
'NotoKufi', child: Stack(
),fontSize: 14)), children: [
], LineChart(
); LineChartData(
}).toList(), minY: chartMinY,
), maxY: chartMaxY,
lineBarsData: lineBars,
clipData: FlClipData.all(), // make sure this doesn't clip the left side
// Add space for tooltip/dot
// extraLeftTitlesReservedWidth: 24,
titlesData: FlTitlesData(
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
const style = TextStyle(
color: Colors.black,
fontSize: 10,
);
// Show only for full years
if (selectedYears.contains(value.toInt())) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// Top tick mark (simulated using a container)
Container(
width: 1,
height: 8,
color: Colors.black,
margin: EdgeInsets.only(bottom: 2),
),
Text(value.toInt().toString(), style: style),
],
);
}
return const SizedBox.shrink();
},
reservedSize: 30,
interval: 1,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: false,
// interval: 5,
reservedSize: 90,
getTitlesWidget: (value, _) =>
Text('${value.toInt()}', style: TextStyle(fontSize: 12)),
),
),
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
borderData: FlBorderData(
show: true,
border: const Border(
left: BorderSide.none,
bottom: BorderSide(color: Colors.black),
),
),
gridData: FlGridData(show: false),
lineTouchData: LineTouchData(
enabled: true,
touchTooltipData: LineTouchTooltipData(
// tooltipBgColor: Colors.black.withOpacity(0.7),
fitInsideHorizontally: true,
fitInsideVertically: true,
getTooltipItems: (spots) {
return spots.map((spot) {
final x = spot.x.toInt();
// if (x == minX || x == maxX) return null;
return LineTooltipItem(
formatNumber(spot.y),
const TextStyle(color: Colors.white),
);
}).toList();
},
),
),
),
),
...lineBars.expand((barData) {
final List<FlSpot> spots = barData.spots;
final color = barData.color ?? Colors.black;
return spots.map((spot) {
final xPos = ((spot.x - minX) / (maxX - minX)) * chartWidth;
final yPos = (1 - (spot.y - chartMinY) / (chartMaxY - chartMinY)) * chartHeight;
// Prevent label from going outside on the left
double adjustedLeft = math.max(0, xPos - 30);
return Stack(
children: [
Positioned(
left: adjustedLeft, // fine-tune horizontal position
top: yPos - 60, // fine-tune vertical position
child: Text(
formatNumber(spot.y),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.w500,
// color: color,
color: Colors.black,
),
),
)
]
);
});
}).toList(),
],
),
),
SizedBox(height: 12),
Wrap( // <-- Legend added here
spacing: 12,
runSpacing: 8,
children: groupByValues.map((group) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(width: 12, height: 12, color: groupColorMap[group]),
SizedBox(width: 6),
Text(
group,
style: TextStyle(
fontSize: 14,
fontFamily: context.translate('Roboto', 'NotoKufi'),
),
),
],
);
}).toList(),
),
],
);
},
), ),
// Chart ),
],
); );
case 'line_trend_2': case 'line_trend_2':
// final List<Color> uniqueColorsLine_trend_2 = [ final List<Color> uniqueColorsLine_trend_2 = [
// Color(0xFF6097CD), Color(0xFF6097CD),
// Color(0xFFD086A7), Color(0xFFD086A7),
// Color(0xFF98BCE5), ];
// Color(0xFFA7B5C5),
// Color(0xFFBED3EC),
// Color(0xFFD4E3F4),
// ];
Set<int> selectedYears = {1970, 1980, 1990, 2000, 2010, 2020}; Set<int> selectedYears = {1970, 1980, 1990, 2000, 2010, 2020};
Map<String, Color> groupColorMap = {}; Map<String, Color> groupColorMap = {};
int colorIndex = 0; int colorIndex = 0;
// for (String group in groupByValues) {
// // groupColorMap[group] = uniqueColorsLine_trend_2[
// // colorIndex % uniqueColorsLine_trend_2.length];
// groupColorMap[group] = uniqueColors[colorIndex % uniqueColors.length];
// // groupColorMap[group] = adjustColorlineTrend(bodyColor, colorIndex);
// colorIndex++;
// }
// Extract all years from the chart data // Extract all years from the chart data
List<int> years = (chartData['response'] as List<dynamic>) List<int> years = (chartData['response'] as List<dynamic>)
.map<int>( .map<int>(
(entry) => (entry) =>
int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0, int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0,
) )
.toList(); .toList();
if (years.isEmpty) { if (years.isEmpty) {
@ -1482,7 +1655,7 @@ class ChartWidget extends StatelessWidget {
// Filter chart data to include only the selected years // Filter chart data to include only the selected years
List<dynamic> filteredData = List<dynamic> filteredData =
(chartData['response'] as List<dynamic>).where((entry) { (chartData['response'] as List<dynamic>).where((entry) {
int year = int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0; int year = int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0;
return selectedYears.contains(year); return selectedYears.contains(year);
}).toList(); }).toList();
@ -1490,91 +1663,344 @@ class ChartWidget extends StatelessWidget {
Set<double> uniqueXValues = filteredData Set<double> uniqueXValues = filteredData
.map<double>( .map<double>(
(entry) => double.parse(entry['ObsKey']['TIME_PERIOD']), (entry) => double.parse(entry['ObsKey']['TIME_PERIOD']),
) )
.toSet(); .toSet();
print('uniqueXValuesLineTrend2- $uniqueXValues');
var chartBarColors = chartData['chart_bar_color'] ?? {}; var chartBarColors = chartData['chart_bar_color'] ?? {};
Map<String, String> barColorsMap = {}; Map<String, String> barColorsMap = {};
chartBarColors.forEach((key, value) { chartBarColors.forEach((key, value) {
barColorsMap[key] = value.toString(); // Ensure values are strings barColorsMap[key] = value.toString(); // Ensure values are strings
}); });
bool hasBarColors = barColorsMap.isNotEmpty; bool hasBarColors = barColorsMap.isNotEmpty;
var chartBarColorsRaw = chartData['chart_bar_color'] ?? {};
// Convert LinkedMap<dynamic, dynamic> to Map<String, String> List<dynamic> keys = chartBarColors['key'] ?? [];
Map<String, String> chartBarColrs = Map<String, String>.from( List<dynamic> colors = chartBarColors['color'] ?? [];
chartBarColorsRaw,
);
// Now map to Color print('RT1keys - $keys');
Map<String, Color> parsedChartBarColors = chartBarColrs.map(( print('RT1colors - $colors');
key,
value, // Convert to a map for easy lookup
) { Map<String, Color> parsedChartBarColors = {};
return MapEntry(key, _hexToColor(value)); for (int i = 0; i < keys.length; i++) {
}); parsedChartBarColors[keys[i].toString()] = _hexToColor(
colors[i].toString(),
);
}
print('RTLinr1 - $parsedChartBarColors');
for (String group in groupByValues) { for (String group in groupByValues) {
if (parsedChartBarColors.containsKey(group)) { if (parsedChartBarColors.containsKey(group)) {
groupColorMap[group] = groupColorMap[group] =
parsedChartBarColors[group]!; // Directly assign as Color parsedChartBarColors[group]!; // Directly assign as Color
} else { } else {
groupColorMap[group] = groupColorMap[group] =
uniqueColors[colorIndex % uniqueColors.length]; uniqueColors[colorIndex % uniqueColors.length];
} }
colorIndex++; colorIndex++;
} }
// Generate line bars for the chart
List<LineChartBarData> lineBars = lineBarsData2(filteredData);
return Column(
children: [
Text(
chartData['chart_heading'] ?? '', final Map<String, List<Map<String, dynamic>>> groupedData = {};
style: TextStyle(fontFamily: context.translate(
'Roboto', // Step 1: Group data by PLANT_TYPE
'NotoKufi', for (var item in filteredData) {
),fontSize: 14, fontWeight: FontWeight.w400), final plantType = item['ObsKey'][groupByKey];
textAlign: TextAlign.center, groupedData.putIfAbsent(plantType, () => []).add(item);
), }
SizedBox(height: 5),
Text( // Step 2: Convert grouped data into List<LineChartBarData>
'', final List<LineChartBarData> lineBars = [];
style: TextStyle(fontFamily: context.translate( final List<Color> colorPalette = [
'Roboto', Color(0xFF6097CD),
'NotoKufi', Color(0xFFD086A7),
),fontSize: 13, fontWeight: FontWeight.w300), Color(0xFF98BCE5),
textAlign: TextAlign.center, Color(0xFFA7B5C5),
), Color(0xFFBED3EC),
SizedBox(height: 10), Color(0xFFD4E3F4),
// Chart ];
Expanded(
child: SingleChildScrollView( String formatNumber(double value) {
scrollDirection: Axis.horizontal, // Enable horizontal scrolling if (value >= 1e12) {
padding: const EdgeInsets.only(right: 40, top: 20), return '${(value / 1e12).toStringAsFixed(2)}T';
child: SizedBox( } else if (value >= 1e9) {
width: (uniqueXValues.length * 50) + return '${(value / 1e9).toStringAsFixed(2)}B';
50, // Adjust width dynamically } else if (value >= 1e6) {
child: LineChart( return '${(value / 1e6).toStringAsFixed(2)}M';
LineChartData( } else if (value >= 1e3) {
lineTouchData: lineTouchData1(context), return '${(value / 1e3).toStringAsFixed(2)}K';
gridData: gridData(), } else {
titlesData: titlesData1(uniqueXValues,context), return value.toStringAsFixed(2); // No decimals for small numbers
borderData: borderData(), }
lineBarsData: lineBars, }
minX: uniqueXValues.reduce((a, b) => a < b ? a : b),
maxX: uniqueXValues.reduce((a, b) => a > b ? a : b), final getXValue = chartData['chart_type_json']['x'] ?? '';
minY: 0, groupedData.forEach((plantType, entries) {
), // Create a map to store population values by year and gender
), Map<int, Map<String, double>> yearGenderMap = {};
for (var entry in filteredData) {
int year = int.tryParse(entry['ObsKey']['TIME_PERIOD'].toString()) ?? 0;
String gender = entry['ObsKey']['GENDER'];
double value =
double.tryParse(entry['ObsValue']['Value'].toString()) ?? 0.0;
if (!yearGenderMap.containsKey(year)) {
yearGenderMap[year] = {'M': 0.0, 'F': 0.0};
}
if (gender == 'Male' || gender == 'ذكر') {
yearGenderMap[year]!['M'] = value;
} else if (gender == 'Female' || gender == 'أنثى') {
yearGenderMap[year]!['F'] = value;
}
}
// Calculate the ratio (M/F) * 100 for each year
List<FlSpot> spots = [];
yearGenderMap.forEach((year, genderMap) {
if (genderMap['M'] != 0.0 && genderMap['F'] != 0.0) {
double ratio = (genderMap['M']! / genderMap['F']!) * 100;
spots.add(FlSpot(year.toDouble(), ratio));
}
});
final baseColor = groupColorMap[plantType] ?? Colors.grey;
lineBars.add(
LineChartBarData(
spots: spots,
isCurved: true,
barWidth: 3,
// color: groupColorMap[plantType] ?? Colors.grey,
color: baseColor.withOpacity(0.9),
dotData: FlDotData(show: true),
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
colors: [
// baseColor.withOpacity(0.2), // visible base color
// Colors.white, // fade to white
baseColor.withOpacity(0.4), // increase opacity
baseColor.withOpacity(0.0), // fade to transparent (not white)
],
stops: [
0.2, // shift teal color start downward (20% from the top)
1.0, // white at the bottom
],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
), ),
), ),
showingIndicators: List.generate(spots.length, (index) => index),
), ),
], );
colorIndex++;
});
return Scaffold(
backgroundColor: Colors.white,
body: Padding(
padding: const EdgeInsets.all(10),
child: LayoutBuilder(
builder: (context, constraints) {
final chartWidth = constraints.maxWidth;
final chartHeight = constraints.maxHeight;
final allSpots = lineBars.expand((line) => line.spots);
final minY = allSpots.map((e) => e.y).reduce((a, b) => a < b ? a : b);
final maxY = allSpots.map((e) => e.y).reduce((a, b) => a > b ? a : b);
final xValues = allSpots.map((e) => e.x).toList();
final minX = xValues.reduce((a, b) => a < b ? a : b).toInt();
final maxX = xValues.reduce((a, b) => a > b ? a : b).toInt();
final paddingFactor = 0.3; // 10% extra padding
final chartMinY = minY - (maxY - minY) * paddingFactor;
final chartMaxY = maxY + (maxY - minY) * paddingFactor;
return Column(
crossAxisAlignment: CrossAxisAlignment.center, // ensures center alignment
children: [
Padding(
padding: const EdgeInsets.only(top: 0),
child: Text(
chartData['chart_heading'] ?? 'Production Quantity by Crop Type (Metric Tonne)',
style: TextStyle(
fontFamily: context.translate('Roboto', 'NotoKufi'),
fontSize: 14,
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.center,
),
),
// SizedBox(height: 6),
Expanded(
child: Stack(
children: [
LineChart(
LineChartData(
minY: chartMinY,
maxY: chartMaxY,
lineBarsData: lineBars,
clipData: FlClipData.all(), // make sure this doesn't clip the left side
// Add space for tooltip/dot
// extraLeftTitlesReservedWidth: 24,
titlesData: FlTitlesData(
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
const style = TextStyle(
color: Colors.black,
fontSize: 10,
);
// Show only for full years
if (selectedYears.contains(value.toInt())) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
// Top tick mark (simulated using a container)
Container(
width: 1,
height: 8,
color: Colors.black,
margin: EdgeInsets.only(bottom: 2),
),
Text(value.toInt().toString(), style: style),
],
);
}
return const SizedBox.shrink();
},
reservedSize: 30,
interval: 1,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: false,
// interval: 5,
reservedSize: 90,
getTitlesWidget: (value, _) =>
Text('${value.toInt()}', style: TextStyle(fontSize: 12)),
),
),
topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
borderData: FlBorderData(
show: true,
border: const Border(
left: BorderSide.none,
bottom: BorderSide(color: Colors.black),
),
),
gridData: FlGridData(show: false),
lineTouchData: LineTouchData(
enabled: true,
touchTooltipData: LineTouchTooltipData(
// tooltipBgColor: Colors.black.withOpacity(0.7),
fitInsideHorizontally: true,
fitInsideVertically: true,
getTooltipItems: (spots) {
return spots.map((spot) {
final x = spot.x.toInt();
// if (x == minX || x == maxX) return null;
// Use barIndex to determine which line this belongs to
final barIndex = spot.barIndex;
if (lineBars[barIndex].spots.isEmpty) return null;
// Check the group name (plantType) from your groupedData order
final plantType = groupedData.keys.toList()[barIndex];
print('plantType $plantType');
if (plantType != "Male" && plantType != "ذكر") {
return null;
}
return LineTooltipItem(
formatNumber(spot.y),
const TextStyle(color: Colors.white),
);
}).toList();
},
),
),
),
),
...lineBars.expand((barData) {
final List<FlSpot> spots = barData.spots;
final color = barData.color ?? Colors.black;
return spots.map((spot) {
final xPos = ((spot.x - minX) / (maxX - minX)) * chartWidth;
final yPos = (1 - (spot.y - chartMinY) / (chartMaxY - chartMinY)) * chartHeight;
// Prevent label from going outside on the left
double adjustedLeft = math.max(0, xPos - 30);
return Stack(
children: [
Positioned(
left: adjustedLeft, // fine-tune horizontal position
top: yPos - 70, // fine-tune vertical position
child: Text(
formatNumber(spot.y),
textAlign: TextAlign.right,
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.w500,
// color: color,
color: Colors.black,
),
),
)
]
);
});
}).toList(),
],
),
),
SizedBox(height: 12),
// Wrap( // <-- Legend added here
// spacing: 12,
// runSpacing: 8,
// children: groupByValues.map((group) {
// return Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// Container(width: 12, height: 12, color: groupColorMap[group]),
// SizedBox(width: 6),
// Text(
// group,
// style: TextStyle(
// fontSize: 14,
// fontFamily: context.translate('Roboto', 'NotoKufi'),
// ),
// ),
// ],
// );
// }).toList(),
// ),
],
);
},
),
),
); );
case 'bar_chart': case 'bar_chart':
// Extract groupBy values and their corresponding y-axis values // Extract groupBy values and their corresponding y-axis values
@ -1720,7 +2146,7 @@ class ChartWidget extends StatelessWidget {
child: Center( child: Center(
child: SizedBox( child: SizedBox(
width: xAxisData.length * width: xAxisData.length *
(40 + 10), // Bar width + manual spacing (80 + 10), // Bar width + manual spacing
child: BarChart( child: BarChart(
BarChartData( BarChartData(
alignment: BarChartAlignment.spaceAround, alignment: BarChartAlignment.spaceAround,
@ -1844,15 +2270,15 @@ class ChartWidget extends StatelessWidget {
return Padding( return Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 8.0, top: 0,
left: 75.0, left: 0,
), ),
child: SizedBox( child: SizedBox(
width: width:
100, // Limit width to force wrapping 100, // Limit width to force wrapping
child: Transform.rotate( child: Transform.rotate(
// angle: -0.5, // angle: -0.5,
angle: -1.5, angle: 0,
child: TooltipTheme( child: TooltipTheme(
data: TooltipThemeData( data: TooltipThemeData(
@ -1878,7 +2304,7 @@ class ChartWidget extends StatelessWidget {
// title, // title,
displayTitle, displayTitle,
softWrap: true, softWrap: true,
textAlign: TextAlign.right, textAlign: TextAlign.center,
style: TextStyle(fontFamily: context.translate( style: TextStyle(fontFamily: context.translate(
'Roboto', 'Roboto',
'NotoKufi', 'NotoKufi',
@ -1896,7 +2322,7 @@ class ChartWidget extends StatelessWidget {
} }
return Container(); return Container();
}, },
reservedSize: 110, reservedSize: 50,
), ),
), ),
topTitles: AxisTitles( topTitles: AxisTitles(
@ -1931,7 +2357,7 @@ class ChartWidget extends StatelessWidget {
toY: value == 0 ? 0.1 : value, toY: value == 0 ? 0.1 : value,
color: bodyColor, color: bodyColor,
borderRadius: BorderRadius.circular(4), borderRadius: BorderRadius.circular(4),
width: 20, width: 35,
), ),
], ],
showingTooltipIndicators: [0], showingTooltipIndicators: [0],
@ -2219,6 +2645,7 @@ class ChartWidget extends StatelessWidget {
) { ) {
return MapEntry(key, _hexToColor(value)); return MapEntry(key, _hexToColor(value));
}); });
print('parsedChartBarColors1100 $parsedChartBarColors');
if (kDebugMode) { if (kDebugMode) {
print('Converted barColorsMap: $barColorsMap'); print('Converted barColorsMap: $barColorsMap');
@ -4368,6 +4795,7 @@ class ChartWidget extends StatelessWidget {
colorIndex % 2]; // Alternate between two colors colorIndex % 2]; // Alternate between two colors
colorIndex++; // Update index for next bar colorIndex++; // Update index for next bar
} else { } else {
print('check fl chart');
// barColor = chartBarColors[gender] ?? Colors.grey; // Default gender-based color // barColor = chartBarColors[gender] ?? Colors.grey; // Default gender-based color
barColor = chartBarColors[normalizedGender] ?? Colors.grey; barColor = chartBarColors[normalizedGender] ?? Colors.grey;
} }

View File

@ -16,4 +16,4 @@ TextStyle subtitleStyle(BuildContext context) {
TextStyle robotoRegular11(BuildContext context) TextStyle robotoRegular11(BuildContext context)
{return TextStyle(fontFamily: context.translate('Roboto', 'NotoKufi'), {return TextStyle(fontFamily: context.translate('Roboto', 'NotoKufi'),
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontSize: 11 * 1.1,color: Color(0xFF000000),);} fontSize: 12 * 1.1,color: Color(0xFF000000),);}

View File

@ -1571,7 +1571,7 @@ class InfoCard extends StatelessWidget {
Text( Text(
title, title,
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 13,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
letterSpacing: 0, letterSpacing: 0,
// color: Colors.black, // color: Colors.black,