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,
|
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;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -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,102 +1353,281 @@ class ChartWidget extends StatelessWidget {
|
|||||||
colorIndex++;
|
colorIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate line bars for the chart
|
|
||||||
List<LineChartBarData> lineBars = lineBarsData(
|
|
||||||
filteredData,
|
|
||||||
groupByValues,
|
|
||||||
groupByKey,
|
final Map<String, List<Map<String, dynamic>>> groupedData = {};
|
||||||
parsedChartBarColors,
|
|
||||||
|
// 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),
|
||||||
|
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
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(
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center, // ensures center alignment
|
||||||
children: [
|
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),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.only(top: 0),
|
||||||
child: Wrap(
|
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;
|
||||||
|
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,
|
spacing: 12,
|
||||||
runSpacing: 8,
|
runSpacing: 8,
|
||||||
children: groupByValues.map((group) {
|
children: groupByValues.map((group) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Container(
|
Container(width: 12, height: 12, color: groupColorMap[group]),
|
||||||
width: 12,
|
|
||||||
height: 12,
|
|
||||||
color: groupColorMap[group],
|
|
||||||
),
|
|
||||||
SizedBox(width: 6),
|
SizedBox(width: 6),
|
||||||
Text(group, style: TextStyle(fontFamily: context.translate(
|
Text(
|
||||||
'Roboto',
|
group,
|
||||||
'NotoKufi',
|
style: TextStyle(
|
||||||
),fontSize: 14)),
|
fontSize: 14,
|
||||||
|
fontFamily: context.translate('Roboto', 'NotoKufi'),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).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>)
|
||||||
@ -1493,29 +1666,29 @@ class ChartWidget extends StatelessWidget {
|
|||||||
)
|
)
|
||||||
.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,
|
|
||||||
|
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(),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
// Now map to Color
|
print('RTLinr1 - $parsedChartBarColors');
|
||||||
Map<String, Color> parsedChartBarColors = chartBarColrs.map((
|
|
||||||
key,
|
|
||||||
value,
|
|
||||||
) {
|
|
||||||
return MapEntry(key, _hexToColor(value));
|
|
||||||
});
|
|
||||||
|
|
||||||
for (String group in groupByValues) {
|
for (String group in groupByValues) {
|
||||||
if (parsedChartBarColors.containsKey(group)) {
|
if (parsedChartBarColors.containsKey(group)) {
|
||||||
@ -1528,54 +1701,307 @@ class ChartWidget extends StatelessWidget {
|
|||||||
colorIndex++;
|
colorIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate line bars for the chart
|
|
||||||
List<LineChartBarData> lineBars = lineBarsData2(filteredData);
|
|
||||||
|
|
||||||
|
|
||||||
|
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(
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center, // ensures center alignment
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Padding(
|
||||||
chartData['chart_heading'] ?? '',
|
padding: const EdgeInsets.only(top: 0),
|
||||||
style: TextStyle(fontFamily: context.translate(
|
child: Text(
|
||||||
'Roboto',
|
chartData['chart_heading'] ?? 'Production Quantity by Crop Type (Metric Tonne)',
|
||||||
'NotoKufi',
|
style: TextStyle(
|
||||||
),fontSize: 14, fontWeight: FontWeight.w400),
|
fontFamily: context.translate('Roboto', 'NotoKufi'),
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w400,
|
||||||
|
),
|
||||||
textAlign: TextAlign.center,
|
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),
|
// SizedBox(height: 6),
|
||||||
// Chart
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SingleChildScrollView(
|
child: Stack(
|
||||||
scrollDirection: Axis.horizontal, // Enable horizontal scrolling
|
children: [
|
||||||
padding: const EdgeInsets.only(right: 40, top: 20),
|
LineChart(
|
||||||
child: SizedBox(
|
|
||||||
width: (uniqueXValues.length * 50) +
|
|
||||||
50, // Adjust width dynamically
|
|
||||||
child: LineChart(
|
|
||||||
LineChartData(
|
LineChartData(
|
||||||
lineTouchData: lineTouchData1(context),
|
minY: chartMinY,
|
||||||
gridData: gridData(),
|
maxY: chartMaxY,
|
||||||
titlesData: titlesData1(uniqueXValues,context),
|
|
||||||
borderData: borderData(),
|
|
||||||
lineBarsData: lineBars,
|
lineBarsData: lineBars,
|
||||||
minX: uniqueXValues.reduce((a, b) => a < b ? a : b),
|
clipData: FlClipData.all(), // make sure this doesn't clip the left side
|
||||||
maxX: uniqueXValues.reduce((a, b) => a > b ? a : b),
|
|
||||||
minY: 0,
|
// 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
|
||||||
List<String> xAxisData = [];
|
List<String> xAxisData = [];
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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),);}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user