chart and card changes

This commit is contained in:
venbaittech 2025-02-14 18:25:18 +05:30
parent dafc089ae9
commit 140ea2405a
4 changed files with 532 additions and 637 deletions

View File

@ -15,12 +15,12 @@ if (localPropertiesFile.exists()) {
def flutterVersionCode = localProperties.getProperty("flutter.versionCode") def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) { if (flutterVersionCode == null) {
flutterVersionCode = "16" flutterVersionCode = "17"
} }
def flutterVersionName = localProperties.getProperty("flutter.versionName") def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) { if (flutterVersionName == null) {
flutterVersionName = "1.0.15" flutterVersionName = "1.0.16"
} }
def keystorePropertiesFile = rootProject.file("key.properties") def keystorePropertiesFile = rootProject.file("key.properties")

View File

@ -51,6 +51,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
List<dynamic> tabFilteredChartData = []; List<dynamic> tabFilteredChartData = [];
List<dynamic> tabFilteredCardData = []; List<dynamic> tabFilteredCardData = [];
List<dynamic> cardData = []; List<dynamic> cardData = [];
List<Map<String, dynamic>> filteredAndSortedData = [];
List<dynamic> originalChartsData = []; List<dynamic> originalChartsData = [];
List<dynamic> originalCardData = []; List<dynamic> originalCardData = [];
List<dynamic> originalTabCardData = []; List<dynamic> originalTabCardData = [];
@ -60,6 +61,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
int _activeTabIndex = 0; int _activeTabIndex = 0;
dynamic _tabsData = []; dynamic _tabsData = [];
// List<Map<String, dynamic>> _tabsData = [];
late List<TargetFocus> marriageTargets; late List<TargetFocus> marriageTargets;
late List<TargetFocus> previousMarriageTargets; late List<TargetFocus> previousMarriageTargets;
late TutorialCoachMark tutorialCoachMark; late TutorialCoachMark tutorialCoachMark;
@ -1186,6 +1188,19 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
// }); // });
// } // }
void processCardData(cardData){
print('PRocessCardData');
setState(() {
filteredAndSortedData = (cardData as List)
.where((card) => (card as Map<String, dynamic>)['is_chart'] == 'false')
.map((card) => card as Map<String, dynamic>)
.toList()
..sort((a, b) => (a['order_id'] as int).compareTo(b['order_id'] as int));
});
}
void processChartData(chartsData) { void processChartData(chartsData) {
// Group data by 'kpi' // Group data by 'kpi'
Map<String, List<Map<String, dynamic>>> groupedData = {}; Map<String, List<Map<String, dynamic>>> groupedData = {};
@ -1201,12 +1216,15 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
// Format 'kpi' values for _tabs with tab_heading // Format 'kpi' values for _tabs with tab_heading
List<Map<String, String>> _tabs = groupedData.entries.map((entry) { List<Map<String, String>> _tabs = groupedData.entries.map((entry) {
String kpi = entry.key; String kpi = entry.key;
var firstChart = entry.value.first;
// Extract tab_heading from the first chart in the grouped list // Extract tab_heading from the first chart in the grouped list
String tabHeading = entry.value.isNotEmpty String tabHeading = entry.value.isNotEmpty
? entry.value.first['tab_heading'] ?? 'Unknown' ? entry.value.first['tab_heading'] ?? 'Unknown'
: 'Unknown'; : 'Unknown';
int tabOrder = int.tryParse(firstChart['tab_order'] ?? '0') ?? 0;
String formattedKpi = kpi String formattedKpi = kpi
.split('_') // Split by underscore .split('_') // Split by underscore
.map((word) => word.isNotEmpty .map((word) => word.isNotEmpty
@ -1214,9 +1232,20 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
: '') // Capitalize : '') // Capitalize
.join(' '); // Join words with space .join(' '); // Join words with space
return {'id': kpi, 'name': tabHeading}; return {'id': kpi, 'name': tabHeading, 'order': tabOrder.toString()};
}).toList(); }).toList();
// Sort tabs by tab_order
_tabs.sort((a, b) {
int orderA = int.tryParse(a['order'] ?? '0') ?? 0;
int orderB = int.tryParse(b['order'] ?? '0') ?? 0;
return orderA.compareTo(orderB);
});
print('Sorted Tabs: $_tabs');
print('Grouped Data: $groupedData'); print('Grouped Data: $groupedData');
print('Tabs: $_tabs'); print('Tabs: $_tabs');
@ -1252,8 +1281,10 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
cardData = nonChartData; cardData = nonChartData;
chartScreenData = data['chartScreenData'] ?? {}; chartScreenData = data['chartScreenData'] ?? {};
processCardData(cardData);
processChartData(chartsData); processChartData(chartsData);
print('chartsData :- $chartsData'); print('chartsData :- $chartsData');
print('cardData :- $cardData'); print('cardData :- $cardData');
print('chartScreenData :- $chartScreenData'); print('chartScreenData :- $chartScreenData');
@ -1274,7 +1305,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
setState(() { setState(() {
chartsData = chartsData =
List.from(originalTabChartsData); // Restore the original data List.from(originalTabChartsData); // Restore the original data
cardData = List.from(originalTabCardData); // Restore the original data // cardData = List.from(originalTabCardData); // Restore the original data
}); });
print('Returning as no filters are selected'); print('Returning as no filters are selected');
Navigator.pop(context); Navigator.pop(context);
@ -1326,47 +1357,47 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
} }
} }
List filteredCardData = []; // List filteredCardData = [];
for (var chart in dataCard) { // for (var chart in dataCard) {
Map<String, dynamic> chartData = Map<String, dynamic>.from(chart); // Map<String, dynamic> chartData = Map<String, dynamic>.from(chart);
// Extract response data for filtering // // Extract response data for filtering
List response = chartData['response'] ?? []; // List response = chartData['response'] ?? [];
// Filter the response based on selected filters // // Filter the response based on selected filters
var cardFilteredData = response.where((responseItem) { // var cardFilteredData = response.where((responseItem) {
final obsKey = responseItem['ObsKey']; // final obsKey = responseItem['ObsKey'];
// Check if each selected filter's `filter_data` matches `ObsKey` values // // Check if each selected filter's `filter_data` matches `ObsKey` values
return selectedFilters.every((filter) { // return selectedFilters.every((filter) {
final filterKey = filter['filter_key']; // final filterKey = filter['filter_key'];
final filterValues = filter['filter_data']; // final filterValues = filter['filter_data'];
// Ensure the filter_key exists in the `ObsKey` and check if it matches any of the `filter_data` // // Ensure the filter_key exists in the `ObsKey` and check if it matches any of the `filter_data`
if (obsKey.containsKey(filterKey)) { // if (obsKey.containsKey(filterKey)) {
final obsKeyValue = obsKey[filterKey]?.toString(); // final obsKeyValue = obsKey[filterKey]?.toString();
return filterValues.isEmpty || filterValues.contains(obsKeyValue); // return filterValues.isEmpty || filterValues.contains(obsKeyValue);
} // }
return false; // return false;
}); // });
}).toList(); // }).toList();
//
// If any data matches the filter, add the whole chart data object // // If any data matches the filter, add the whole chart data object
if (cardFilteredData.isNotEmpty) { // if (cardFilteredData.isNotEmpty) {
filteredCardData.add({ // filteredCardData.add({
...chart, // Include all other properties of the chart object // ...chart, // Include all other properties of the chart object
'response': cardFilteredData, // Only include filtered response data // 'response': cardFilteredData, // Only include filtered response data
}); // });
} // }
} // }
// Update the chartsData with the filtered data // Update the chartsData with the filtered data
setState(() { setState(() {
chartsData = filteredData; chartsData = filteredData;
cardData = filteredCardData; // Adjust this part as needed // cardData = filteredCardData; // Adjust this part as needed
}); });
print("Filtered Data: $filteredData"); print("Filtered Data: $filteredData");
print("Filtered Card: $filteredCardData"); // print("Filtered Card: $filteredCardData");
// Go back after applying filters // Go back after applying filters
Navigator.pop(context); Navigator.pop(context);
@ -1856,6 +1887,8 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
// return 1.0; // Default fallback (should never be reached) // return 1.0; // Default fallback (should never be reached)
// } // }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final locale = ref.watch(localeProvider); final locale = ref.watch(localeProvider);
@ -1945,7 +1978,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
// height: myheight / 5, // height: myheight / 5,
width: double.infinity, width: double.infinity,
// color: color, // color: color,
color: Color(int.parse(chartScreenData['header_color'].replaceFirst('#', '0xff'))), color: Color(int.parse((chartScreenData['header_color']?? '#898C81').replaceFirst('#', '0xff'))),
child: Padding( child: Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
left: 30, right: 30, top: 8, bottom: 5), left: 30, right: 30, top: 8, bottom: 5),
@ -1959,7 +1992,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
color: Colors.white, color: Colors.white,
fontSize: 26, fontSize: 26,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
fontFamily: 'Roboto' fontFamily: 'Roboto'
), ),
), ),
Container( Container(
@ -1999,7 +2032,7 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
), ),
)), )),
Container( Container(
color: Color(int.parse(chartScreenData['header_color'].replaceFirst('#', '0xff'))), color: Color(int.parse((chartScreenData['header_color']?? '#898C81').replaceFirst('#', '0xff'))),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
@ -2099,15 +2132,25 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
color: Colors.white, color: Colors.white,
), ),
), ),
GestureDetector(
IconButton( onTap: () {
icon: Icon(Icons.filter_alt_outlined, showRightSideModal(context, filterData, chartsData);
color: Colors.white, size: 24),
onPressed: () {
showRightSideModal(
context, filterData, chartsData);
}, },
), child: Image.asset(
UaeNumbersAssetPath.filterUae,
color: Colors.white, // Set color to white
width: 21,
height: 21,
),
)
// IconButton(
// icon: Icon(Icons.filter_alt_outlined,
// color: Colors.white, size: 24),
// onPressed: () {
// showRightSideModal(
// context, filterData, chartsData);
// },
// ),
], ],
), ),
@ -2120,12 +2163,11 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
), ),
Expanded( Expanded(
child: Container( child: Container(
color: Color(int.parse(chartScreenData['body_color'].replaceFirst('#', '0xff'))), color: Color(int.parse((chartScreenData['body_color']?? '#898C81').replaceFirst('#', '0xff'))),
child: Padding( child: Padding(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
child: ListView( child: ListView(
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -2181,23 +2223,34 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
gridDelegate: gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount( SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: cardData.length == 2 // crossAxisCount:2,
? 2 crossAxisCount: cardData.length % 2 == 0 ? 2 : 3,
: cardData.length == 3 // crossAxisCount: cardData.length == 2
? 3 // ? 2
: cardData.length == 4 // : cardData.length == 3
? 2 // ? 3
: 3, // Default to 3 if more than 4 items // 2 cards per row // : cardData.length == 4
// ? 2
// : 3, // Default to 3 if more than 4 items // 2 cards per row
crossAxisSpacing: 2, crossAxisSpacing: 2,
mainAxisSpacing: 5, mainAxisSpacing: 5,
childAspectRatio: childAspectRatio: MediaQuery.of(context).size.width /
calculateAspectRatio(cardData.length, cardData), (MediaQuery.of(context).size.height / (cardData.length % 2 == 0 ? 2 : 1.3)),
// childAspectRatio:
// calculateAspectRatio(cardData.length, cardData),
), ),
itemBuilder: (context, index) {
final item = cardData[index]; // Sort and filter the cardData
itemBuilder: (context, index) {
final item = filteredAndSortedData[index];
// print('item');
// final item = cardData[index];
print('item item item $item'); print('item item item $item');
final chart_type = item['chart_type']; final chart_type = item['chart_type'];
final chart_heading = item['chart_heading']; final chart_heading = item['chart_heading'];
final response = item['response'] as List<dynamic>? ?? [];
final card_logo = item['card_logo']; final card_logo = item['card_logo'];
final data = apiService.processNonChartData(item); final data = apiService.processNonChartData(item);
print('processNonChartData'); print('processNonChartData');
@ -2207,354 +2260,241 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
? 180.0 ? 180.0
: 130.0; : 130.0;
if (chart_type == 'total') { if (response.isEmpty) {
return return const SizedBox.shrink();
Container( // return Container(
margin: const EdgeInsets.all(10), // color: Colors.redAccent,
decoration: BoxDecoration( // );
borderRadius: BorderRadius.circular(12), }
border: Border.all( if (response.length == 1) {
color: Color(int.parse(chartScreenData['border_color'].replaceFirst('#', '0xff'))), // Dynamic border color return Container(
width: 1, // Adjust border thickness margin: const EdgeInsets.all(10),
), decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Color(int.parse(
(chartScreenData['border_color']?? '#898C81')
.replaceFirst('#', '0xff'))),
// Dynamic border color
width: 1, // Adjust border thickness
), ),
child: Card( ),
margin: const EdgeInsets.all(0), child: Card(
shape: RoundedRectangleBorder( margin: const EdgeInsets.all(0),
borderRadius: BorderRadius.circular(12), shape: RoundedRectangleBorder(
), borderRadius: BorderRadius.circular(12),
elevation: 0, // Remove shadow to keep only the outli ),
elevation: 2,
child: Container( child: Container(
height: cardHeight, height: cardHeight,
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.only(left:16.0, right: 16.0, bottom: 1.0 , top: 1.0),
child: Column( child: Column(
mainAxisSize: MainAxisSize mainAxisSize: MainAxisSize.min,
.min, // Adjust card height based on content mainAxisAlignment: MainAxisAlignment
mainAxisAlignment: MainAxisAlignment.center, .center,
children: [ children: [
// const Icon(Icons.public, Image.network(
// color: Color(0xFF90B0D5), size: 30), card_logo ?? '',
Image.network( width: 30,
card_logo ?? '', height: 30,
width: 30, errorBuilder:
height: 30, (context, error, stackTrace) {
errorBuilder: return Icon(Icons.public,
(context, error, stackTrace) { color: Color(0xFF90B0D5),
return Icon(Icons.public, size: 30); // Fallback icon
color: Color(0xFF90B0D5), },
size: 30); // Fallback icon ),
}, const SizedBox(height: 1),
), Flexible(
const SizedBox(height: 3), fit: FlexFit.loose,
Flexible( // child: FittedBox(
fit: FlexFit.loose,
child: FittedBox(
child: SizedBox(
// height: 70.0,
child: Text( child: Text(
'${chart_heading ?? 'NA'}', '${chart_heading ?? 'NA'}',
textAlign: TextAlign.center, textAlign: TextAlign.center,
maxLines: 2, // Limit to 2 lines maxLines: 2,
// Limit to 2 lines
softWrap: softWrap:
true, // Enable soft wrapping true,
// Enable soft wrapping
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully // overflow: TextOverflow.ellipsis, // Handle overflow gracefully
style: TextStyle( style: TextStyle(
fontSize: 8, fontSize: 9,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Colors.black87, color: Colors.black87,
), ),
), ),
// ),
), ),
), const SizedBox(height: 5),
), Flexible(
const SizedBox(height: 1), fit: FlexFit.loose,
Text( child: FittedBox(
'(${data['lastYear'] ?? 'NA'})', child: Text(
style: const TextStyle( '(${ response[0]['display_value'] ?? 'NA'})',
fontSize: 10, color: Colors.grey), // '(${data['firstYear'] ??
), // 'NA'} - ${data['lastYear'] ??
const SizedBox(height: 1), // 'NA'})',
Flexible( style: const TextStyle(
fit: FlexFit.loose, fontSize: 10,
child: FittedBox( color: Colors.grey),
fit: BoxFit.contain, ),),),
child: Text( const SizedBox(height: 1),
apiService.formatAmount( Flexible(
data['lastYearValue']), fit: FlexFit.loose,
style: const TextStyle( child: SizedBox(
fontSize: 18, height: 50.0,
fontWeight: FontWeight.w900, child: FittedBox(
color: Color(0xFF90B0D5), fit: BoxFit.contain,
), child: Text(
), response[0]['value'] ?? 'NA',
), // '${data['roundedAverage'] ??
), // 'NA'}',
SizedBox( style: const TextStyle(
height: 2, // Height of the divider fontSize: 20,
child: Divider( fontWeight: FontWeight
color: Colors.grey, .w900,
thickness: color: Color(0xFF90B0D5),
1, // Divider line thickness ),
), ),
),
Flexible(
fit: FlexFit.loose,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
apiService.formatAmount(
data['secondLastYearValue']),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFFD83731),
),
),
),
),
Text(
'(${data['secondLastYear'] ?? 'NA'})',
style: const TextStyle(
fontSize: 9,
fontWeight: FontWeight.w500,
color: Colors.grey),
),
],
),
),
),);
} else if (chart_type == 'average') {
return Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Color(int.parse(chartScreenData['border_color'].replaceFirst('#', '0xff'))), // Dynamic border color
width: 1, // Adjust border thickness
),
),
child: Card(
margin: const EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 4,
child: Container(
height: cardHeight,
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.network(
card_logo ?? '',
width: 30,
height: 30,
errorBuilder:
(context, error, stackTrace) {
return Icon(Icons.public,
color: Color(0xFF90B0D5),
size: 30); // Fallback icon
},
),
const SizedBox(height: 1),
Flexible(
fit: FlexFit.loose,
// child: FittedBox(
child: Text(
'${chart_heading ?? 'NA'}',
textAlign: TextAlign.center,
maxLines: 2, // Limit to 2 lines
softWrap:
true, // Enable soft wrapping
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully
style: TextStyle(
fontSize: 9,
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
// ),
),
const SizedBox(height: 5),
Flexible(
fit: FlexFit.loose,
child: FittedBox(
child:Text(
'(${data['firstYear'] ?? 'NA'} - ${data['lastYear'] ?? 'NA'})',
style: const TextStyle(
fontSize: 10, color: Colors.grey),
),),),
const SizedBox(height: 1),
Flexible(
fit: FlexFit.loose,
child: SizedBox(
height: 50.0,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
'${data['roundedAverage'] ?? 'NA'}',
style: const TextStyle(
fontSize: 20,
fontWeight: FontWeight.w900,
color: Color(0xFF90B0D5),
), ),
), ),
), ),
), ],
), ),
],
),
),
));
} else if (chart_type == 'totals') {
return Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Color(int.parse(chartScreenData['border_color'].replaceFirst('#', '0xff'))), // Dynamic border color
width: 1, // Adjust border thickness
), ),
), ));
child: Card(
margin: const EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 4,
child: Container(
height: cardHeight,
// height: maxHeight,
// padding: const EdgeInsets.all(10.0),
padding: const EdgeInsets.symmetric(
vertical: 0, horizontal: 10),
child: Column(
// mainAxisSize: MainAxisSize.min, // Adjust card height based on content
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.public,
color: Color(0xFF90B0D5), size: 30),
const SizedBox(height: 3),
Flexible(
fit: FlexFit.loose,
child: FittedBox(
child: Text(
'${chart_heading ?? 'NA'}',
textAlign: TextAlign.center,
maxLines: 2, // Limit to 2 lines
softWrap:
true, // Enable soft wrapping
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
),
),
Text(
'(${data['lastYear'] ?? 'NA'})',
style: const TextStyle(
fontSize: 11, color: Colors.grey),
),
Flexible(
fit: FlexFit.loose,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
apiService.formatAmount(
data['lastYearValue']),
style: const TextStyle(
fontSize: 22,
fontWeight: FontWeight.w900,
color: Color(0xFF90B0D5),
),
),
),
),
],
),
),
));
} else if (chart_type == 'averages') {
return Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Color(int.parse(chartScreenData['border_color'].replaceFirst('#', '0xff'))), // Dynamic border color
width: 1, // Adjust border thickness
),
),
child: Card(
margin: const EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 4,
child: Container(
// height: maxHeight,
// height: 200,
height: cardHeight,
padding: const EdgeInsets.all(5.0),
// padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 10),
child: Column(
// mainAxisSize: MainAxisSize.min, // Adjust card height based on content
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(Icons.public,
color: Color(0xFF90B0D5), size: 30),
const SizedBox(height: 5),
Text(
'${chart_heading ?? 'NA'}',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
Text(
'(${data['lastYear'] ?? 'NA'})',
style: const TextStyle(
fontSize: 11, color: Colors.grey),
),
const SizedBox(height: 5),
Flexible(
fit: FlexFit.loose,
child: FittedBox(
child: Text(
'${chart_heading ?? 'NA'}',
textAlign: TextAlign.center,
maxLines: 2, // Limit to 2 lines
softWrap:
true, // Enable soft wrapping
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully
style: TextStyle(
fontSize: 11,
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
),
),
],
),
),
));
} else {
return const SizedBox
.shrink(); // Ignore unknown KPIs
} }
else{
return
Container(
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: Color(int.parse((chartScreenData['border_color']?? '#898C81').replaceFirst('#', '0xff'))), // Dynamic border color
width: 1, // Adjust border thickness
),
),
child: Card(
margin: const EdgeInsets.all(0),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0, // Remove shadow to keep only the outli
child: Container(
height: cardHeight,
padding: const EdgeInsets.all(10.0),
child: Column(
mainAxisSize: MainAxisSize
.min, // Adjust card height based on content
mainAxisAlignment: MainAxisAlignment.center,
children: [
// const Icon(Icons.public,
// color: Color(0xFF90B0D5), size: 30),
Image.network(
card_logo ?? '',
width: 30,
height: 30,
errorBuilder:
(context, error, stackTrace) {
return Icon(Icons.public,
color: Color(0xFF90B0D5),
size: 30); // Fallback icon
},
),
const SizedBox(height: 3),
Flexible(
fit: FlexFit.loose,
child: FittedBox(
child: SizedBox(
// height: 70.0,
child: Text(
'${chart_heading ?? 'NA'}',
// "TOTAL",
textAlign: TextAlign.center,
maxLines: 2, // Limit to 2 lines
softWrap:
true, // Enable soft wrapping
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully
style: TextStyle(
fontSize: 8,
fontWeight: FontWeight.w400,
color: Colors.black87,
),
),
),
),
),
const SizedBox(height: 1),
Text(
'(${response[0]['display_value'] ?? 'NA'})',
// '(${data['lastYear'] ?? 'NA'})',
style: const TextStyle(
fontSize: 10, color: Colors.grey),
),
const SizedBox(height: 1),
Flexible(
fit: FlexFit.loose,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
response[0]['value'] ?? 'NA',
// apiService.formatAmount(
// data['lastYearValue']),
// apiService.formatAmount(
// data['lastYearValue']),
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w900,
color: Color(0xFF90B0D5),
),
),
),
),
SizedBox(
height: 2, // Height of the divider
child: Divider(
color: Color(0xFFBBBCBD),
thickness:
1, // Divider line thickness
),
),
Flexible(
fit: FlexFit.loose,
child: FittedBox(
fit: BoxFit.contain,
child: Text(
response[1]['value'] ?? 'NA',
// apiService.formatAmount(
// data['secondLastYearValue']),
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFFD83731),
),
),
),
),
Text(
'(${response[1]['display_value'] ?? 'NA'})',
// '(${data['secondLastYear'] ?? 'NA'})',
style: const TextStyle(
fontSize: 9,
fontWeight: FontWeight.w500,
color: Colors.grey),
),
],
),
),
),);
}
}, },
), ),
), ),
@ -2572,8 +2512,11 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 10), const SizedBox(height: 10),
Container( ConstrainedBox(
height: 300, constraints: const BoxConstraints(
minHeight: 200, // Minimum height
maxHeight: 400, // Maximum height
),
// child: buildChart(chartsData[index]), // child: buildChart(chartsData[index]),
child: ChartWidget( child: ChartWidget(
chartData: chartsData[index])), chartData: chartsData[index])),

View File

@ -261,21 +261,23 @@ class ChartWidget extends StatelessWidget {
), ),
), ),
SizedBox(height: 25), SizedBox(height: 25),
Flexible( Flexible(
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
child: Container( child: Container(
constraints: BoxConstraints(minHeight: 5), // Allow dynamic height constraints:
BoxConstraints(minHeight: 5), // Allow dynamic height
child: Padding( child: Padding(
padding: const EdgeInsets.only(left: 8.0 ,right: 8.0,bottom: 8.0,top:20.0), padding: const EdgeInsets.only(
child: Wrap( left: 8.0, right: 8.0, bottom: 8.0, top: 20.0),
spacing: 12, child: Wrap(
runSpacing: 8, spacing: 12,
children: generateIndicators(chartData, chartData['group_by']), runSpacing: 8,
), children:
), generateIndicators(chartData, chartData['group_by']),
), ),
),
),
), ),
), ),
], ],
@ -896,7 +898,22 @@ class ChartWidget extends StatelessWidget {
? yAxisData.reduce((a, b) => a > b ? a : b) * 1.2 ? yAxisData.reduce((a, b) => a > b ? a : b) * 1.2
: 10, : 10,
rotationQuarterTurns: rotationTurns, rotationQuarterTurns: rotationTurns,
barTouchData: BarTouchData(enabled: true), barTouchData: BarTouchData(
enabled: true,
handleBuiltInTouches: true,
touchTooltipData: BarTouchTooltipData(
fitInsideHorizontally: true,
fitInsideVertically: true,
tooltipPadding: const EdgeInsets.all(8),
tooltipMargin: 16,
getTooltipItem: (group, groupIndex, rod, rodIndex) {
return BarTooltipItem(
'${rod.toY.toStringAsFixed(1)}',
const TextStyle(color: Colors.white),
);
},
),
),
titlesData: FlTitlesData( titlesData: FlTitlesData(
leftTitles: AxisTitles( leftTitles: AxisTitles(
sideTitles: SideTitles( sideTitles: SideTitles(
@ -925,11 +942,11 @@ class ChartWidget extends StatelessWidget {
// angle: -45 * // angle: -45 *
// (3.1415927 / 180), // Rotating by -45 degrees // (3.1415927 / 180), // Rotating by -45 degrees
alignment: Alignment.center, alignment: Alignment.center,
child: Center( child: Center(
child: SizedBox( child: SizedBox(
width: 100, width: 100,
child: Text( child: Text(
xAxisData[value.toInt()], xAxisData[value.toInt()],
style: const TextStyle(fontSize: 12), style: const TextStyle(fontSize: 12),
softWrap: true, softWrap: true,
maxLines: 2, maxLines: 2,
@ -977,39 +994,15 @@ class ChartWidget extends StatelessWidget {
const double barWidth = 30; // Width for each bar, including spacing const double barWidth = 30; // Width for each bar, including spacing
return totalBars * barWidth; // Calculate total chart width return totalBars * barWidth; // Calculate total chart width
} }
String cropKey = chartData['chart_type_json']['x_sub_group'];
// Group crops by CROP_TYPE // Group crops by CROP_TYPE
Map<String, List<String>> groupedCrops = {}; Map<String, List<String>> groupedCrops = {};
int touchedGroupIndex = -1; int touchedGroupIndex = -1;
// Iterate over the chartData to group crops by CROP_TYPE // Iterate over the chartData to group crops by CROP_TYPE
for (var item in chartData['response']) { for (var item in chartData['response']) {
String crop, cropType; String crop, cropType;
if (chartData['dataset'] == 'health_services') { crop = item['ObsKey'][cropKey];
crop = item['ObsKey']['SECTOR']; cropType = item['ObsKey'][groupByKey];
cropType = item['ObsKey'][groupByKey];
} else if (chartData['dataset'] == 'general_education') {
print(chartData['dataset'] == 'general_education');
crop = item['ObsKey']['GENDER'];
cropType = item['ObsKey'][groupByKey];
} else if (chartData['dataset'] == 'higher_education') {
crop = item['ObsKey']['GENDER'];
cropType = item['ObsKey'][groupByKey];
} else if (chartData['dataset'] == 'labour_force') {
crop = item['ObsKey']['GENDER'];
cropType = item['ObsKey'][groupByKey];
} else if (chartData['dataset'] == 'clinics') {
if (kDebugMode) {
print('forclinics');
print('ObsKey: ${item['ObsKey']}');
// print('NR_TYPE: ${item['ObsKey']?['NR_TYPE']}');
}
crop = item['ObsKey']['MEASURE'];
cropType = item['ObsKey'][groupByKey];
} else {
crop = item['ObsKey']['CROP'];
cropType = item['ObsKey'][groupByKey];
}
if (groupedCrops.containsKey(cropType)) { if (groupedCrops.containsKey(cropType)) {
groupedCrops[cropType]!.add(crop); groupedCrops[cropType]!.add(crop);
@ -1187,64 +1180,17 @@ class ChartWidget extends StatelessWidget {
]); ]);
case 'horizontal_rotate': case 'horizontal_rotate':
String cropKey = chartData['chart_type_json']
['y_sub_group']; // Dynamically get crop key
// Group crops by CROP_TYPE // Group crops by CROP_TYPE
Map<String, List<String>> groupedCrops = {}; Map<String, List<String>> groupedCrops = {};
// Iterate over the chartData to group crops by CROP_TYPE // Iterate over the chartData to group crops by CROP_TYPE
for (var item in chartData['response']) { for (var item in chartData['response']) {
String crop, cropType; String crop, cropType;
if (chartData['dataset'] == 'natural_reserves') { // crop = item['ObsKey']['CROP'];
crop = item['ObsKey']['NR_TYPE']; crop = item['ObsKey'][cropKey];
cropType = item['ObsKey'][groupByKey]; cropType = item['ObsKey'][groupByKey];
}
else if(chartData['dataset'] == 'gdp')
{
print("ForGDP");
if (kDebugMode) {
print('ObsKey: ${item['ObsKey']}');
// print('NR_TYPE: ${item['ObsKey']?['NR_TYPE']}');
}
crop = item['ObsKey']['BOP_ITEM'];
cropType = item['ObsKey'][groupByKey];
} else if(chartData['dataset'] == 'air_transport')
{
print("Forair_transport");
if (kDebugMode) {
print('ObsKey: ${item['ObsKey']}');
// print('NR_TYPE: ${item['ObsKey']?['NR_TYPE']}');
}
crop = item['ObsKey']['MEASURE'];
cropType = item['ObsKey'][groupByKey];
} else if(chartData['dataset'] == 'import')
{
print("Forimport");
if (kDebugMode) {
print('ObsKey: ${item['ObsKey']}');
// print('NR_TYPE: ${item['ObsKey']?['NR_TYPE']}');
}
crop = item['ObsKey']['COUNTRY'];
cropType = item['ObsKey'][groupByKey];
}
else if(chartData['dataset'] == 'gdpq')
{
print("ForGDPq");
if (kDebugMode) {
print('ObsKey: ${item['ObsKey']}');
}
crop = item['ObsKey']['MEASURE'];
// crop = item['ObsKey']['TIME_PERIOD'];
cropType = item['ObsKey'][groupByKey];
}
else {
crop = item['ObsKey']['CROP'];
cropType = item['ObsKey'][groupByKey];
}
if (groupedCrops.containsKey(cropType)) { if (groupedCrops.containsKey(cropType)) {
groupedCrops[cropType]!.add(crop); groupedCrops[cropType]!.add(crop);
@ -1257,16 +1203,17 @@ class ChartWidget extends StatelessWidget {
int rotationTurns = 1; int rotationTurns = 1;
print('Grouped Crops: $groupedCrops'); print('Grouped Crops: $groupedCrops');
int numberOfBars = groupedCrops.length; // Number of grouped sets (bar groups) int numberOfBars =
groupedCrops.length; // Number of grouped sets (bar groups)
double barHeight = 40.0; // Height per individual bar double barHeight = 40.0; // Height per individual bar
double barSpacing = 30.0; // Space between bar sets double barSpacing = 30.0; // Space between bar sets
double minHeight = 300.0; // Minimum chart height double minHeight = 300.0; // Minimum chart height
double maxHeight = 1000.0; // Maximum chart height double maxHeight = 1000.0; // Maximum chart height
// Calculate total height dynamically // Calculate total height dynamically
double chartHeight = ((numberOfBars * barHeight) + ((numberOfBars - 1) * barSpacing)) double chartHeight =
.clamp(minHeight, maxHeight); ((numberOfBars * barHeight) + ((numberOfBars - 1) * barSpacing))
.clamp(minHeight, maxHeight);
return Column(children: [ return Column(children: [
Text( Text(
@ -1280,166 +1227,172 @@ class ChartWidget extends StatelessWidget {
SizedBox(height: 10), SizedBox(height: 10),
// Chart // Chart
Expanded( Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.vertical,
child: SingleChildScrollView( child: SingleChildScrollView(
scrollDirection: Axis.horizontal, scrollDirection: Axis.vertical,
child:SizedBox( child: SingleChildScrollView(
width: 1000, scrollDirection: Axis.horizontal,
height: chartHeight, child: SizedBox(
child: BarChart( width: 1000,
BarChartData( height: chartHeight,
maxY: 400000, child: BarChart(
rotationQuarterTurns: rotationTurns, BarChartData(
barTouchData: BarTouchData( maxY: 400000,
touchTooltipData: BarTouchTooltipData( rotationQuarterTurns: rotationTurns,
tooltipHorizontalAlignment: FLHorizontalAlignment.center, barTouchData: BarTouchData(
tooltipRoundedRadius: 8, touchTooltipData: BarTouchTooltipData(
fitInsideHorizontally: tooltipHorizontalAlignment:
true, // Ensure it fits within the screen FLHorizontalAlignment.center,
fitInsideVertically: true, tooltipRoundedRadius: 8,
tooltipPadding: EdgeInsets.all(8), fitInsideHorizontally:
tooltipMargin: 16, true, // Ensure it fits within the screen
// Only show tooltip when touched fitInsideVertically: true,
getTooltipItem: (group, groupIndex, rod, rodIndex) { tooltipPadding: EdgeInsets.all(8),
if (rod.toY == 0 || touchedGroupIndex == -1) { tooltipMargin: 16,
return null; // Don't show the tooltip if the value is 0 or there's no touch // Only show tooltip when touched
} getTooltipItem: (group, groupIndex, rod, rodIndex) {
if (rod.toY == 0 || touchedGroupIndex == -1) {
return null; // Don't show the tooltip if the value is 0 or there's no touch
}
if (groupIndex == touchedGroupIndex) { if (groupIndex == touchedGroupIndex) {
// print('Group Index: $groupIndex, Group : $group'); // print('Group Index: $groupIndex, Group : $group');
// Get the group label dynamically // Get the group label dynamically
String groupLabel = groupByValues.elementAt(groupIndex); String groupLabel =
groupByValues.elementAt(groupIndex);
// Fetch the crop for the current group from groupedCrops // Fetch the crop for the current group from groupedCrops
String cropType = groupByValues.elementAt(groupIndex); String cropType =
String crop = groupedCrops[cropType]![rodIndex]; groupByValues.elementAt(groupIndex);
double value = rod.toY; String crop = groupedCrops[cropType]![rodIndex];
double value = rod.toY;
String formattedValue; String formattedValue;
if (value >= 1000000) { if (value >= 1000000) {
formattedValue = formattedValue =
(value / 1000000).toStringAsFixed(1) + 'M'; (value / 1000000).toStringAsFixed(1) + 'M';
} else if (value >= 1000) { } else if (value >= 1000) {
formattedValue = formattedValue =
(value / 1000).toStringAsFixed(1) + 'K'; (value / 1000).toStringAsFixed(1) + 'K';
} else { } else {
formattedValue = value.toStringAsFixed( formattedValue = value.toStringAsFixed(
0); // for values smaller than 1000 0); // for values smaller than 1000
} }
return BarTooltipItem( return BarTooltipItem(
'$groupLabel\n$crop', '$groupLabel\n$crop',
const TextStyle( const TextStyle(
color: Colors.white, color: Colors.white,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
),
children: [
TextSpan(
text: ' Value: $formattedValue',
style: const TextStyle(
color: Colors.yellow,
fontWeight: FontWeight.w500,
), ),
), children: [
], TextSpan(
); text: ' Value: $formattedValue',
} style: const TextStyle(
return null; color: Colors.yellow,
}, fontWeight: FontWeight.w500,
), ),
touchCallback: (event, response) {
if (event.isInterestedForInteractions &&
response != null &&
response.spot != null) {
// setState(() {
touchedGroupIndex = response.spot!.touchedBarGroupIndex;
// });
} else {
// setState(() {
touchedGroupIndex = -1; // Reset if no interaction
// });
}
},
),
titlesData: FlTitlesData(
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: false,
reservedSize: 20,
interval: 100000,
getTitlesWidget: (value, meta) {
return Text(
value.toInt().toString(),
style: const TextStyle(fontSize: 12),
);
},
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 80, // Added space for rotated titles
getTitlesWidget: (value, meta) {
if (value < groupByValues.length) {
String title = groupByValues.elementAt(value.toInt());
return Transform.rotate(
angle: -1.58, // Rotation in radians (~ -30 degrees)
child: Center(
child: SizedBox(
width: 80,
child: Text(
title,
style: const TextStyle(fontSize: 12),
softWrap: true,
maxLines: 2,
), ),
), ],
), );
); }
return null;
},
),
touchCallback: (event, response) {
if (event.isInterestedForInteractions &&
response != null &&
response.spot != null) {
// setState(() {
touchedGroupIndex =
response.spot!.touchedBarGroupIndex;
// });
} else {
// setState(() {
touchedGroupIndex = -1; // Reset if no interaction
// });
} }
return const SizedBox.shrink();
}, },
), ),
), titlesData: FlTitlesData(
rightTitles: leftTitles: AxisTitles(
AxisTitles(sideTitles: SideTitles(showTitles: false)), sideTitles: SideTitles(
topTitles: showTitles: false,
AxisTitles(sideTitles: SideTitles(showTitles: false)), reservedSize: 20,
), interval: 100000,
borderData: FlBorderData( getTitlesWidget: (value, meta) {
show: true, return Text(
border: const Border( value.toInt().toString(),
// left: BorderSide(color: Colors.grey), style: const TextStyle(fontSize: 12),
bottom: BorderSide(color: Colors.white), );
},
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 80, // Added space for rotated titles
getTitlesWidget: (value, meta) {
if (value < groupByValues.length) {
String title =
groupByValues.elementAt(value.toInt());
return Transform.rotate(
angle:
-1.58, // Rotation in radians (~ -30 degrees)
child: Center(
child: SizedBox(
width: 80,
child: Text(
title,
style: const TextStyle(fontSize: 12),
softWrap: true,
maxLines: 2,
),
),
),
);
}
return const SizedBox.shrink();
},
),
),
rightTitles:
AxisTitles(sideTitles: SideTitles(showTitles: false)),
topTitles:
AxisTitles(sideTitles: SideTitles(showTitles: false)),
),
borderData: FlBorderData(
show: true,
border: const Border(
// left: BorderSide(color: Colors.grey),
bottom: BorderSide(color: Colors.white),
),
),
gridData: FlGridData(
show: false,
drawVerticalLine: true,
verticalInterval: 1,
horizontalInterval: 100000,
getDrawingHorizontalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.5),
strokeWidth: 1,
);
},
getDrawingVerticalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.5),
strokeWidth: 1,
);
},
),
barGroups: _buildHorizontalRotateBarGroups(
chartData, groupByValues),
alignment: BarChartAlignment.spaceAround,
), ),
), ),
gridData: FlGridData(
show: false,
drawVerticalLine: true,
verticalInterval: 1,
horizontalInterval: 100000,
getDrawingHorizontalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.5),
strokeWidth: 1,
);
},
getDrawingVerticalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.5),
strokeWidth: 1,
);
},
),
barGroups:
_buildHorizontalRotateBarGroups(chartData, groupByValues),
alignment: BarChartAlignment.spaceAround,
), ),
), ),
),
),
)), )),
// Expanded( // Expanded(
// child: Stack( // child: Stack(
@ -2109,9 +2062,8 @@ class ChartWidget extends StatelessWidget {
chartData['dataset'] == 'higher_education' || chartData['dataset'] == 'higher_education' ||
chartData['dataset'] == 'air_transport' || chartData['dataset'] == 'air_transport' ||
chartData['dataset'] == 'labour_force' || chartData['dataset'] == 'labour_force' ||
chartData['dataset'] == 'gdp' chartData['dataset'] == 'gdp' ||
) { chartData['dataset'] == 'clinics') {
barColor = uniqueColorsForTwo[colorIndex % uniqueColorsForTwo.length]; barColor = uniqueColorsForTwo[colorIndex % uniqueColorsForTwo.length];
colorIndex++; colorIndex++;
} else { } else {

View File

@ -1,7 +1,7 @@
name: uae_stat name: uae_stat
description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness." description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness."
publish_to: "none" publish_to: "none"
version: 1.0.15+16 version: 1.0.16+17
environment: environment:
sdk: ">=3.2.3 <4.0.0" sdk: ">=3.2.3 <4.0.0"