bhavin feedback changes
This commit is contained in:
parent
ebf5b5e853
commit
f6c14eb2e5
@ -1462,6 +1462,10 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
|
||||
void applyFilters(BuildContext context, List filters, List data,
|
||||
List dataCard, List selectedFilters) {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
});
|
||||
Navigator.pop(context);
|
||||
print('applyFilters called');
|
||||
print('Selected ApplyFilters: $selectedFilters');
|
||||
|
||||
@ -1505,7 +1509,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
tabWiseKpi, // Ensure you are passing the correct KPI here
|
||||
formattedFilters // Pass the formatted filter data here
|
||||
);
|
||||
Navigator.pop(context);
|
||||
// Navigator.pop(context);
|
||||
|
||||
selectedFiltersStorage = List.from(selectedFilters);
|
||||
print('selectedFiltersStoraged- $selectedFiltersStorage');
|
||||
@ -1999,7 +2003,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
Navigator.pop(context);
|
||||
} else {
|
||||
setState(() {
|
||||
isLoading = true;
|
||||
// isLoading = true;
|
||||
chartsData = originalTabChartsData;
|
||||
cardData = originalTabCardData;
|
||||
});
|
||||
|
||||
@ -90,20 +90,28 @@ class ChartWidget extends StatelessWidget {
|
||||
// Function to get colors for both English & Arabic keys
|
||||
List<Color> get uniqueColors {
|
||||
final Map<String, String> translations = {
|
||||
'الاقتصاد': 'ECONOMY',
|
||||
'الاجتماعي': 'SOCIAL',
|
||||
'البيئة': 'ENVIRONMENT',
|
||||
'اقتصاد': 'ECONOMY',
|
||||
'اجتماعي': 'SOCIAL',
|
||||
'بيئة': 'ENVIRONMENT',
|
||||
};
|
||||
|
||||
print('color check');
|
||||
print(translations);
|
||||
print('chartHeader $chartHeader');
|
||||
print(translations[chartHeader]);
|
||||
|
||||
|
||||
String key = translations[chartHeader] ?? chartHeader;
|
||||
print(key);
|
||||
print(colorMap[key]);
|
||||
return colorMap[key] ?? [Colors.grey];
|
||||
}
|
||||
|
||||
List<Color> get uniqueColorsForTwo {
|
||||
final Map<String, String> translations = {
|
||||
'الاقتصاد': 'ECONOMY',
|
||||
'الاجتماعي': 'SOCIAL',
|
||||
'البيئة': 'ENVIRONMENT',
|
||||
'اقتصاد': 'ECONOMY',
|
||||
'اجتماعي': 'SOCIAL',
|
||||
'بيئة': 'ENVIRONMENT',
|
||||
};
|
||||
|
||||
String key = translations[chartHeader] ?? chartHeader;
|
||||
@ -112,9 +120,9 @@ class ChartWidget extends StatelessWidget {
|
||||
|
||||
List<Color> get uniqueColorsForThree {
|
||||
final Map<String, String> translations = {
|
||||
'الاقتصاد': 'ECONOMY',
|
||||
'الاجتماعي': 'SOCIAL',
|
||||
'البيئة': 'ENVIRONMENT',
|
||||
'اقتصاد': 'ECONOMY',
|
||||
'اجتماعي': 'SOCIAL',
|
||||
'بيئة': 'ENVIRONMENT',
|
||||
};
|
||||
|
||||
String key = translations[chartHeader] ?? chartHeader;
|
||||
@ -1271,12 +1279,7 @@ class ChartWidget extends StatelessWidget {
|
||||
Set<int> selectedYears = {1970, 1980, 1990, 2000, 2010, 2020};
|
||||
Map<String, Color> groupColorMap = {};
|
||||
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
|
||||
List<int> years = (chartData['response'] as List<dynamic>)
|
||||
@ -1322,16 +1325,7 @@ class ChartWidget extends StatelessWidget {
|
||||
barColorsMap[key] = value.toString(); // Ensure values are strings
|
||||
});
|
||||
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> colors = chartBarColors['color'] ?? [];
|
||||
@ -1359,109 +1353,288 @@ class ChartWidget extends StatelessWidget {
|
||||
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
|
||||
padding: const EdgeInsets.only(right: 40, top: 10),
|
||||
child: SizedBox(
|
||||
width: (uniqueXValues.length * 50) +
|
||||
50, // Adjust width dynamically
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: lineTouchData1(context),
|
||||
gridData: gridData(),
|
||||
titlesData: titlesData1(uniqueXValues,context),
|
||||
borderData: borderData(),
|
||||
lineBarsData: lineBars,
|
||||
minX: uniqueXValues.reduce((a, b) => a < b ? a : b),
|
||||
maxX: uniqueXValues.reduce((a, b) => a > b ? a : b),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
final Map<String, List<Map<String, dynamic>>> groupedData = {};
|
||||
|
||||
// Step 1: Group data by PLANT_TYPE
|
||||
for (var item in filteredData) {
|
||||
final plantType = item['ObsKey'][groupByKey];
|
||||
groupedData.putIfAbsent(plantType, () => []).add(item);
|
||||
}
|
||||
|
||||
// Step 2: Convert grouped data into List<LineChartBarData>
|
||||
final List<LineChartBarData> lineBars = [];
|
||||
final List<Color> colorPalette = [
|
||||
Color(0xFF6097CD),
|
||||
Color(0xFFD086A7),
|
||||
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),
|
||||
child: Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 8,
|
||||
children: groupByValues.map((group) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
color: groupColorMap[group],
|
||||
);
|
||||
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(width: 6),
|
||||
Text(group, style: TextStyle(fontFamily: context.translate(
|
||||
'Roboto',
|
||||
'NotoKufi',
|
||||
),fontSize: 14)),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
// 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;
|
||||
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':
|
||||
// final List<Color> uniqueColorsLine_trend_2 = [
|
||||
// Color(0xFF6097CD),
|
||||
// Color(0xFFD086A7),
|
||||
// Color(0xFF98BCE5),
|
||||
// Color(0xFFA7B5C5),
|
||||
// Color(0xFFBED3EC),
|
||||
// Color(0xFFD4E3F4),
|
||||
// ];
|
||||
final List<Color> uniqueColorsLine_trend_2 = [
|
||||
Color(0xFF6097CD),
|
||||
Color(0xFFD086A7),
|
||||
];
|
||||
Set<int> selectedYears = {1970, 1980, 1990, 2000, 2010, 2020};
|
||||
Map<String, Color> groupColorMap = {};
|
||||
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
|
||||
List<int> years = (chartData['response'] as List<dynamic>)
|
||||
.map<int>(
|
||||
(entry) =>
|
||||
int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0,
|
||||
)
|
||||
int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0,
|
||||
)
|
||||
.toList();
|
||||
|
||||
if (years.isEmpty) {
|
||||
@ -1482,7 +1655,7 @@ class ChartWidget extends StatelessWidget {
|
||||
|
||||
// Filter chart data to include only the selected years
|
||||
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;
|
||||
return selectedYears.contains(year);
|
||||
}).toList();
|
||||
@ -1490,91 +1663,344 @@ class ChartWidget extends StatelessWidget {
|
||||
Set<double> uniqueXValues = filteredData
|
||||
.map<double>(
|
||||
(entry) => double.parse(entry['ObsKey']['TIME_PERIOD']),
|
||||
)
|
||||
)
|
||||
.toSet();
|
||||
|
||||
print('uniqueXValuesLineTrend2- $uniqueXValues');
|
||||
|
||||
var chartBarColors = chartData['chart_bar_color'] ?? {};
|
||||
Map<String, String> barColorsMap = {};
|
||||
|
||||
chartBarColors.forEach((key, value) {
|
||||
barColorsMap[key] = value.toString(); // Ensure values are strings
|
||||
});
|
||||
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,
|
||||
);
|
||||
List<dynamic> keys = chartBarColors['key'] ?? [];
|
||||
List<dynamic> colors = chartBarColors['color'] ?? [];
|
||||
|
||||
// Now map to Color
|
||||
Map<String, Color> parsedChartBarColors = chartBarColrs.map((
|
||||
key,
|
||||
value,
|
||||
) {
|
||||
return MapEntry(key, _hexToColor(value));
|
||||
});
|
||||
print('RT1keys - $keys');
|
||||
print('RT1colors - $colors');
|
||||
|
||||
// Convert to a map for easy lookup
|
||||
Map<String, Color> parsedChartBarColors = {};
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
parsedChartBarColors[keys[i].toString()] = _hexToColor(
|
||||
colors[i].toString(),
|
||||
);
|
||||
}
|
||||
print('RTLinr1 - $parsedChartBarColors');
|
||||
|
||||
for (String group in groupByValues) {
|
||||
if (parsedChartBarColors.containsKey(group)) {
|
||||
groupColorMap[group] =
|
||||
parsedChartBarColors[group]!; // Directly assign as Color
|
||||
parsedChartBarColors[group]!; // Directly assign as Color
|
||||
} else {
|
||||
groupColorMap[group] =
|
||||
uniqueColors[colorIndex % uniqueColors.length];
|
||||
uniqueColors[colorIndex % uniqueColors.length];
|
||||
}
|
||||
colorIndex++;
|
||||
}
|
||||
|
||||
// Generate line bars for the chart
|
||||
List<LineChartBarData> lineBars = lineBarsData2(filteredData);
|
||||
|
||||
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(
|
||||
'',
|
||||
style: TextStyle(fontFamily: context.translate(
|
||||
'Roboto',
|
||||
'NotoKufi',
|
||||
),fontSize: 13, fontWeight: FontWeight.w300),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
// Chart
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal, // Enable horizontal scrolling
|
||||
padding: const EdgeInsets.only(right: 40, top: 20),
|
||||
child: SizedBox(
|
||||
width: (uniqueXValues.length * 50) +
|
||||
50, // Adjust width dynamically
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: lineTouchData1(context),
|
||||
gridData: gridData(),
|
||||
titlesData: titlesData1(uniqueXValues,context),
|
||||
borderData: borderData(),
|
||||
lineBarsData: lineBars,
|
||||
minX: uniqueXValues.reduce((a, b) => a < b ? a : b),
|
||||
maxX: uniqueXValues.reduce((a, b) => a > b ? a : b),
|
||||
minY: 0,
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
|
||||
final Map<String, List<Map<String, dynamic>>> groupedData = {};
|
||||
|
||||
// Step 1: Group data by PLANT_TYPE
|
||||
for (var item in filteredData) {
|
||||
final plantType = item['ObsKey'][groupByKey];
|
||||
groupedData.putIfAbsent(plantType, () => []).add(item);
|
||||
}
|
||||
|
||||
// Step 2: Convert grouped data into List<LineChartBarData>
|
||||
final List<LineChartBarData> lineBars = [];
|
||||
final List<Color> colorPalette = [
|
||||
Color(0xFF6097CD),
|
||||
Color(0xFFD086A7),
|
||||
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) {
|
||||
// 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':
|
||||
// Extract groupBy values and their corresponding y-axis values
|
||||
@ -1720,7 +2146,7 @@ class ChartWidget extends StatelessWidget {
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: xAxisData.length *
|
||||
(40 + 10), // Bar width + manual spacing
|
||||
(80 + 10), // Bar width + manual spacing
|
||||
child: BarChart(
|
||||
BarChartData(
|
||||
alignment: BarChartAlignment.spaceAround,
|
||||
@ -1844,15 +2270,15 @@ class ChartWidget extends StatelessWidget {
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 8.0,
|
||||
left: 75.0,
|
||||
top: 0,
|
||||
left: 0,
|
||||
),
|
||||
child: SizedBox(
|
||||
width:
|
||||
100, // Limit width to force wrapping
|
||||
child: Transform.rotate(
|
||||
// angle: -0.5,
|
||||
angle: -1.5,
|
||||
angle: 0,
|
||||
|
||||
child: TooltipTheme(
|
||||
data: TooltipThemeData(
|
||||
@ -1878,7 +2304,7 @@ class ChartWidget extends StatelessWidget {
|
||||
// title,
|
||||
displayTitle,
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.right,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontFamily: context.translate(
|
||||
'Roboto',
|
||||
'NotoKufi',
|
||||
@ -1896,7 +2322,7 @@ class ChartWidget extends StatelessWidget {
|
||||
}
|
||||
return Container();
|
||||
},
|
||||
reservedSize: 110,
|
||||
reservedSize: 50,
|
||||
),
|
||||
),
|
||||
topTitles: AxisTitles(
|
||||
@ -1931,7 +2357,7 @@ class ChartWidget extends StatelessWidget {
|
||||
toY: value == 0 ? 0.1 : value,
|
||||
color: bodyColor,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
width: 20,
|
||||
width: 35,
|
||||
),
|
||||
],
|
||||
showingTooltipIndicators: [0],
|
||||
@ -2219,6 +2645,7 @@ class ChartWidget extends StatelessWidget {
|
||||
) {
|
||||
return MapEntry(key, _hexToColor(value));
|
||||
});
|
||||
print('parsedChartBarColors1100 $parsedChartBarColors');
|
||||
|
||||
if (kDebugMode) {
|
||||
print('Converted barColorsMap: $barColorsMap');
|
||||
@ -4368,6 +4795,7 @@ class ChartWidget extends StatelessWidget {
|
||||
colorIndex % 2]; // Alternate between two colors
|
||||
colorIndex++; // Update index for next bar
|
||||
} else {
|
||||
print('check fl chart');
|
||||
// barColor = chartBarColors[gender] ?? Colors.grey; // Default gender-based color
|
||||
barColor = chartBarColors[normalizedGender] ?? Colors.grey;
|
||||
}
|
||||
|
||||
@ -16,4 +16,4 @@ TextStyle subtitleStyle(BuildContext context) {
|
||||
TextStyle robotoRegular11(BuildContext context)
|
||||
{return TextStyle(fontFamily: context.translate('Roboto', 'NotoKufi'),
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 11 * 1.1,color: Color(0xFF000000),);}
|
||||
fontSize: 12 * 1.1,color: Color(0xFF000000),);}
|
||||
|
||||
@ -1571,7 +1571,7 @@ class InfoCard extends StatelessWidget {
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w400,
|
||||
letterSpacing: 0,
|
||||
// color: Colors.black,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user