diff --git a/android/app/build.gradle b/android/app/build.gradle index bc59d713..2e87a3f1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -15,12 +15,12 @@ if (localPropertiesFile.exists()) { def flutterVersionCode = localProperties.getProperty("flutter.versionCode") if (flutterVersionCode == null) { - flutterVersionCode = "16" + flutterVersionCode = "17" } def flutterVersionName = localProperties.getProperty("flutter.versionName") if (flutterVersionName == null) { - flutterVersionName = "1.0.15" + flutterVersionName = "1.0.16" } def keystorePropertiesFile = rootProject.file("key.properties") diff --git a/lib/presentation/Screens/charts/screens/chart_screen.dart b/lib/presentation/Screens/charts/screens/chart_screen.dart index afd5b95e..dcc97e71 100644 --- a/lib/presentation/Screens/charts/screens/chart_screen.dart +++ b/lib/presentation/Screens/charts/screens/chart_screen.dart @@ -51,6 +51,7 @@ class _ChartScreen1State extends ConsumerState { List tabFilteredChartData = []; List tabFilteredCardData = []; List cardData = []; + List> filteredAndSortedData = []; List originalChartsData = []; List originalCardData = []; List originalTabCardData = []; @@ -60,6 +61,7 @@ class _ChartScreen1State extends ConsumerState { int _activeTabIndex = 0; dynamic _tabsData = []; + // List> _tabsData = []; late List marriageTargets; late List previousMarriageTargets; late TutorialCoachMark tutorialCoachMark; @@ -1186,6 +1188,19 @@ class _ChartScreen1State extends ConsumerState { // }); // } + void processCardData(cardData){ + + print('PRocessCardData'); + setState(() { + filteredAndSortedData = (cardData as List) + .where((card) => (card as Map)['is_chart'] == 'false') + .map((card) => card as Map) + .toList() + ..sort((a, b) => (a['order_id'] as int).compareTo(b['order_id'] as int)); + }); + } + + void processChartData(chartsData) { // Group data by 'kpi' Map>> groupedData = {}; @@ -1201,12 +1216,15 @@ class _ChartScreen1State extends ConsumerState { // Format 'kpi' values for _tabs with tab_heading List> _tabs = groupedData.entries.map((entry) { String kpi = entry.key; + var firstChart = entry.value.first; // Extract tab_heading from the first chart in the grouped list String tabHeading = entry.value.isNotEmpty ? entry.value.first['tab_heading'] ?? 'Unknown' : 'Unknown'; + int tabOrder = int.tryParse(firstChart['tab_order'] ?? '0') ?? 0; + String formattedKpi = kpi .split('_') // Split by underscore .map((word) => word.isNotEmpty @@ -1214,9 +1232,20 @@ class _ChartScreen1State extends ConsumerState { : '') // Capitalize .join(' '); // Join words with space - return {'id': kpi, 'name': tabHeading}; + return {'id': kpi, 'name': tabHeading, 'order': tabOrder.toString()}; }).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('Tabs: $_tabs'); @@ -1252,8 +1281,10 @@ class _ChartScreen1State extends ConsumerState { cardData = nonChartData; chartScreenData = data['chartScreenData'] ?? {}; + processCardData(cardData); processChartData(chartsData); + print('chartsData :- $chartsData'); print('cardData :- $cardData'); print('chartScreenData :- $chartScreenData'); @@ -1274,7 +1305,7 @@ class _ChartScreen1State extends ConsumerState { setState(() { chartsData = 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'); Navigator.pop(context); @@ -1326,47 +1357,47 @@ class _ChartScreen1State extends ConsumerState { } } - List filteredCardData = []; - for (var chart in dataCard) { - Map chartData = Map.from(chart); - // Extract response data for filtering - List response = chartData['response'] ?? []; + // List filteredCardData = []; + // for (var chart in dataCard) { + // Map chartData = Map.from(chart); + // // Extract response data for filtering + // List response = chartData['response'] ?? []; - // Filter the response based on selected filters - var cardFilteredData = response.where((responseItem) { - final obsKey = responseItem['ObsKey']; + // // Filter the response based on selected filters + // var cardFilteredData = response.where((responseItem) { + // final obsKey = responseItem['ObsKey']; - // Check if each selected filter's `filter_data` matches `ObsKey` values - return selectedFilters.every((filter) { - final filterKey = filter['filter_key']; - final filterValues = filter['filter_data']; + // // Check if each selected filter's `filter_data` matches `ObsKey` values + // return selectedFilters.every((filter) { + // final filterKey = filter['filter_key']; + // final filterValues = filter['filter_data']; - // Ensure the filter_key exists in the `ObsKey` and check if it matches any of the `filter_data` - if (obsKey.containsKey(filterKey)) { - final obsKeyValue = obsKey[filterKey]?.toString(); - return filterValues.isEmpty || filterValues.contains(obsKeyValue); - } - return false; - }); - }).toList(); - - // If any data matches the filter, add the whole chart data object - if (cardFilteredData.isNotEmpty) { - filteredCardData.add({ - ...chart, // Include all other properties of the chart object - 'response': cardFilteredData, // Only include filtered response data - }); - } - } + // // Ensure the filter_key exists in the `ObsKey` and check if it matches any of the `filter_data` + // if (obsKey.containsKey(filterKey)) { + // final obsKeyValue = obsKey[filterKey]?.toString(); + // return filterValues.isEmpty || filterValues.contains(obsKeyValue); + // } + // return false; + // }); + // }).toList(); +// + // // If any data matches the filter, add the whole chart data object + // if (cardFilteredData.isNotEmpty) { + // filteredCardData.add({ + // ...chart, // Include all other properties of the chart object + // 'response': cardFilteredData, // Only include filtered response data + // }); + // } + // } // Update the chartsData with the filtered data setState(() { chartsData = filteredData; - cardData = filteredCardData; // Adjust this part as needed + // cardData = filteredCardData; // Adjust this part as needed }); print("Filtered Data: $filteredData"); - print("Filtered Card: $filteredCardData"); + // print("Filtered Card: $filteredCardData"); // Go back after applying filters Navigator.pop(context); @@ -1856,6 +1887,8 @@ class _ChartScreen1State extends ConsumerState { // return 1.0; // Default fallback (should never be reached) // } + + @override Widget build(BuildContext context) { final locale = ref.watch(localeProvider); @@ -1945,7 +1978,7 @@ class _ChartScreen1State extends ConsumerState { // height: myheight / 5, width: double.infinity, // color: color, - color: Color(int.parse(chartScreenData['header_color'].replaceFirst('#', '0xff'))), + color: Color(int.parse((chartScreenData['header_color']?? '#898C81').replaceFirst('#', '0xff'))), child: Padding( padding: EdgeInsets.only( left: 30, right: 30, top: 8, bottom: 5), @@ -1959,7 +1992,7 @@ class _ChartScreen1State extends ConsumerState { color: Colors.white, fontSize: 26, fontWeight: FontWeight.w400, - fontFamily: 'Roboto' + fontFamily: 'Roboto' ), ), Container( @@ -1999,7 +2032,7 @@ class _ChartScreen1State extends ConsumerState { ), )), Container( - color: Color(int.parse(chartScreenData['header_color'].replaceFirst('#', '0xff'))), + color: Color(int.parse((chartScreenData['header_color']?? '#898C81').replaceFirst('#', '0xff'))), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ @@ -2099,15 +2132,25 @@ class _ChartScreen1State extends ConsumerState { color: Colors.white, ), ), - - IconButton( - icon: Icon(Icons.filter_alt_outlined, - color: Colors.white, size: 24), - onPressed: () { - showRightSideModal( - context, filterData, chartsData); + GestureDetector( + onTap: () { + 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 { ), Expanded( child: Container( - color: Color(int.parse(chartScreenData['body_color'].replaceFirst('#', '0xff'))), + color: Color(int.parse((chartScreenData['body_color']?? '#898C81').replaceFirst('#', '0xff'))), child: Padding( padding: const EdgeInsets.all(10.0), - child: ListView( + child: ListView( children: [ - Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -2181,23 +2223,34 @@ class _ChartScreen1State extends ConsumerState { physics: const NeverScrollableScrollPhysics(), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: cardData.length == 2 - ? 2 - : cardData.length == 3 - ? 3 - : cardData.length == 4 - ? 2 - : 3, // Default to 3 if more than 4 items // 2 cards per row + // crossAxisCount:2, + crossAxisCount: cardData.length % 2 == 0 ? 2 : 3, + // crossAxisCount: cardData.length == 2 + // ? 2 + // : cardData.length == 3 + // ? 3 + // : cardData.length == 4 + // ? 2 + // : 3, // Default to 3 if more than 4 items // 2 cards per row crossAxisSpacing: 2, mainAxisSpacing: 5, - childAspectRatio: - calculateAspectRatio(cardData.length, cardData), + childAspectRatio: MediaQuery.of(context).size.width / +(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'); final chart_type = item['chart_type']; final chart_heading = item['chart_heading']; + final response = item['response'] as List? ?? []; final card_logo = item['card_logo']; final data = apiService.processNonChartData(item); print('processNonChartData'); @@ -2207,354 +2260,241 @@ class _ChartScreen1State extends ConsumerState { ? 180.0 : 130.0; - if (chart_type == 'total') { - 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 - ), + if (response.isEmpty) { + return const SizedBox.shrink(); + // return Container( + // color: Colors.redAccent, + // ); + } + if (response.length == 1) { + 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: Card( + margin: const EdgeInsets.all(0), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + elevation: 2, + child: Container( + height: cardHeight, + padding: const EdgeInsets.only(left:16.0, right: 16.0, bottom: 1.0 , top: 1.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 + maxLines: 2, + // Limit to 2 lines softWrap: - true, // Enable soft wrapping + true, + // Enable soft wrapping // overflow: TextOverflow.ellipsis, // Handle overflow gracefully style: TextStyle( - fontSize: 8, + fontSize: 9, fontWeight: FontWeight.w400, color: Colors.black87, ), ), + // ), ), - ), - ), - const SizedBox(height: 1), - Text( - '(${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( - 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: Colors.grey, - thickness: - 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), + const SizedBox(height: 5), + Flexible( + fit: FlexFit.loose, + child: FittedBox( + child: Text( + '(${ response[0]['display_value'] ?? 'NA'})', + // '(${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( + response[0]['value'] ?? 'NA', + // '${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 { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 10), - Container( - height: 300, + ConstrainedBox( + constraints: const BoxConstraints( + minHeight: 200, // Minimum height + maxHeight: 400, // Maximum height + ), // child: buildChart(chartsData[index]), child: ChartWidget( chartData: chartsData[index])), diff --git a/lib/presentation/Screens/charts/widgets/chart_widget.dart b/lib/presentation/Screens/charts/widgets/chart_widget.dart index d27c4c88..5ed4864c 100644 --- a/lib/presentation/Screens/charts/widgets/chart_widget.dart +++ b/lib/presentation/Screens/charts/widgets/chart_widget.dart @@ -261,21 +261,23 @@ class ChartWidget extends StatelessWidget { ), ), SizedBox(height: 25), - Flexible( child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Container( - constraints: BoxConstraints(minHeight: 5), // Allow dynamic height + constraints: + BoxConstraints(minHeight: 5), // Allow dynamic height child: Padding( - padding: const EdgeInsets.only(left: 8.0 ,right: 8.0,bottom: 8.0,top:20.0), - child: Wrap( - spacing: 12, - runSpacing: 8, - children: generateIndicators(chartData, chartData['group_by']), - ), - ), - ), + padding: const EdgeInsets.only( + left: 8.0, right: 8.0, bottom: 8.0, top: 20.0), + child: Wrap( + spacing: 12, + 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 : 10, 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( leftTitles: AxisTitles( sideTitles: SideTitles( @@ -925,11 +942,11 @@ class ChartWidget extends StatelessWidget { // angle: -45 * // (3.1415927 / 180), // Rotating by -45 degrees alignment: Alignment.center, - child: Center( + child: Center( child: SizedBox( width: 100, child: Text( - xAxisData[value.toInt()], + xAxisData[value.toInt()], style: const TextStyle(fontSize: 12), softWrap: true, maxLines: 2, @@ -977,39 +994,15 @@ class ChartWidget extends StatelessWidget { const double barWidth = 30; // Width for each bar, including spacing return totalBars * barWidth; // Calculate total chart width } + String cropKey = chartData['chart_type_json']['x_sub_group']; // Group crops by CROP_TYPE Map> groupedCrops = {}; int touchedGroupIndex = -1; // Iterate over the chartData to group crops by CROP_TYPE for (var item in chartData['response']) { String crop, cropType; - if (chartData['dataset'] == 'health_services') { - crop = item['ObsKey']['SECTOR']; - 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]; - } + crop = item['ObsKey'][cropKey]; + cropType = item['ObsKey'][groupByKey]; if (groupedCrops.containsKey(cropType)) { groupedCrops[cropType]!.add(crop); @@ -1187,64 +1180,17 @@ class ChartWidget extends StatelessWidget { ]); case 'horizontal_rotate': + String cropKey = chartData['chart_type_json'] + ['y_sub_group']; // Dynamically get crop key // Group crops by CROP_TYPE Map> groupedCrops = {}; // Iterate over the chartData to group crops by CROP_TYPE for (var item in chartData['response']) { String crop, cropType; - if (chartData['dataset'] == 'natural_reserves') { - crop = item['ObsKey']['NR_TYPE']; - 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]; - } + // crop = item['ObsKey']['CROP']; + crop = item['ObsKey'][cropKey]; + cropType = item['ObsKey'][groupByKey]; if (groupedCrops.containsKey(cropType)) { groupedCrops[cropType]!.add(crop); @@ -1257,16 +1203,17 @@ class ChartWidget extends StatelessWidget { int rotationTurns = 1; 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 barSpacing = 30.0; // Space between bar sets double minHeight = 300.0; // Minimum chart height double maxHeight = 1000.0; // Maximum chart height // Calculate total height dynamically - double chartHeight = ((numberOfBars * barHeight) + ((numberOfBars - 1) * barSpacing)) - .clamp(minHeight, maxHeight); - + double chartHeight = + ((numberOfBars * barHeight) + ((numberOfBars - 1) * barSpacing)) + .clamp(minHeight, maxHeight); return Column(children: [ Text( @@ -1280,166 +1227,172 @@ class ChartWidget extends StatelessWidget { SizedBox(height: 10), // Chart Expanded( - child: SingleChildScrollView( - scrollDirection: Axis.vertical, child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child:SizedBox( - width: 1000, - height: chartHeight, - child: BarChart( - BarChartData( - maxY: 400000, - rotationQuarterTurns: rotationTurns, - barTouchData: BarTouchData( - touchTooltipData: BarTouchTooltipData( - tooltipHorizontalAlignment: FLHorizontalAlignment.center, - tooltipRoundedRadius: 8, - fitInsideHorizontally: - true, // Ensure it fits within the screen - fitInsideVertically: true, - tooltipPadding: EdgeInsets.all(8), - tooltipMargin: 16, - // 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 - } + scrollDirection: Axis.vertical, + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: SizedBox( + width: 1000, + height: chartHeight, + child: BarChart( + BarChartData( + maxY: 400000, + rotationQuarterTurns: rotationTurns, + barTouchData: BarTouchData( + touchTooltipData: BarTouchTooltipData( + tooltipHorizontalAlignment: + FLHorizontalAlignment.center, + tooltipRoundedRadius: 8, + fitInsideHorizontally: + true, // Ensure it fits within the screen + fitInsideVertically: true, + tooltipPadding: EdgeInsets.all(8), + tooltipMargin: 16, + // 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) { - // print('Group Index: $groupIndex, Group : $group'); + if (groupIndex == touchedGroupIndex) { + // print('Group Index: $groupIndex, Group : $group'); - // Get the group label dynamically - String groupLabel = groupByValues.elementAt(groupIndex); + // Get the group label dynamically + String groupLabel = + groupByValues.elementAt(groupIndex); - // Fetch the crop for the current group from groupedCrops - String cropType = groupByValues.elementAt(groupIndex); - String crop = groupedCrops[cropType]![rodIndex]; - double value = rod.toY; + // Fetch the crop for the current group from groupedCrops + String cropType = + groupByValues.elementAt(groupIndex); + String crop = groupedCrops[cropType]![rodIndex]; + double value = rod.toY; - String formattedValue; - if (value >= 1000000) { - formattedValue = - (value / 1000000).toStringAsFixed(1) + 'M'; - } else if (value >= 1000) { - formattedValue = - (value / 1000).toStringAsFixed(1) + 'K'; - } else { - formattedValue = value.toStringAsFixed( - 0); // for values smaller than 1000 - } + String formattedValue; + if (value >= 1000000) { + formattedValue = + (value / 1000000).toStringAsFixed(1) + 'M'; + } else if (value >= 1000) { + formattedValue = + (value / 1000).toStringAsFixed(1) + 'K'; + } else { + formattedValue = value.toStringAsFixed( + 0); // for values smaller than 1000 + } - return BarTooltipItem( - '$groupLabel\n$crop', - const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - children: [ - TextSpan( - text: ' Value: $formattedValue', - style: const TextStyle( - color: Colors.yellow, - fontWeight: FontWeight.w500, + return BarTooltipItem( + '$groupLabel\n$crop', + const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, ), - ), - ], - ); - } - 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 - // }); - } - }, - ), - 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, + children: [ + TextSpan( + text: ' Value: $formattedValue', + style: const TextStyle( + color: Colors.yellow, + fontWeight: FontWeight.w500, + ), ), - ), - ), - ); + ], + ); + } + 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(); }, ), - ), - 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), + 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 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( // child: Stack( @@ -2109,9 +2062,8 @@ class ChartWidget extends StatelessWidget { chartData['dataset'] == 'higher_education' || chartData['dataset'] == 'air_transport' || chartData['dataset'] == 'labour_force' || - chartData['dataset'] == 'gdp' - ) { - + chartData['dataset'] == 'gdp' || + chartData['dataset'] == 'clinics') { barColor = uniqueColorsForTwo[colorIndex % uniqueColorsForTwo.length]; colorIndex++; } else { diff --git a/pubspec.yaml b/pubspec.yaml index 956f8eca..c8991e73 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: uae_stat description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness." publish_to: "none" -version: 1.0.15+16 +version: 1.0.16+17 environment: sdk: ">=3.2.3 <4.0.0"