diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index 6449fff3..1678f6e3 100644 --- a/lib/config/my_router.dart +++ b/lib/config/my_router.dart @@ -374,6 +374,7 @@ final GoRouter router = GoRouter( '0xFFFFFFFF'; // Default white final mainTopic = state.uri.queryParameters['mainTopic'] ?? ''; final title = state.uri.queryParameters['title'] ?? ''; + final key = state.uri.queryParameters['key'] ?? ''; print('Router dataSets: $dataSets'); print('Router bgColor: $bgColor'); @@ -386,6 +387,7 @@ final GoRouter router = GoRouter( bgColor: bgColor, mainTopic: mainTopic, title: title, + keyParam: key, ); }, ), diff --git a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart index 190d010f..6917a159 100644 --- a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart +++ b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/uae_numbers.dart @@ -1,7 +1,9 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/components/constant/constant.dart'; @@ -23,28 +25,31 @@ class UaeNumbers extends StatelessWidget { } } -class uaenumberWidget extends StatefulWidget { +class uaenumberWidget extends ConsumerStatefulWidget { const uaenumberWidget({super.key}); @override - _UaenumberWidgetState createState() => _UaenumberWidgetState(); + ConsumerState createState() => _UaenumberWidgetState(); } -class _UaenumberWidgetState extends State { +class _UaenumberWidgetState extends ConsumerState { @override List homePageData = []; bool isLoading = true; + int? expandedIndex = 0; @override void initState() { - fetchData(); + super.initState(); + final locale = ref.read(localeProvider); + fetchData(locale?.languageCode ?? 'en'); } - Future fetchData() async { + Future fetchData(locale) async { // const baseUrl = 'https://pb.venbait.in/api/getHomePageData'; const baseUrl = 'https://pb.venbait.in/api/getUAENumbersData'; try { - final response = await http.get(Uri.parse(baseUrl)); + final response = await http.get(Uri.parse(baseUrl + '?language=$locale')); if (response.statusCode == 200) { setState(() { homePageData = json.decode(response.body); @@ -63,6 +68,10 @@ class _UaenumberWidgetState extends State { } Widget build(BuildContext context) { + ref.listen(localeProvider, (previous, next) { + final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + fetchData(localeCode); + }); double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; @@ -99,7 +108,12 @@ class _UaenumberWidgetState extends State { return CustomExpandableTile( index: index, - isExpanded: isFirstTile, +isExpanded: expandedIndex == index, // Compare with expandedIndex + onTap: (index) { + setState(() { + expandedIndex = (expandedIndex == index) ? null : index; + }); + }, title: mainTopic['main_topic'], titleBackgroundColor: backgroundColor, children: _buildSubTopics(mainTopic['sub_topics'] ?? [], mywidth, @@ -280,6 +294,8 @@ class CustomExpandableTile extends StatefulWidget { final List children; final int index; final bool isExpanded; + final ValueChanged onTap; + const CustomExpandableTile({ required this.title, @@ -287,6 +303,8 @@ class CustomExpandableTile extends StatefulWidget { required this.children, required this.index, required this.isExpanded, + required this.onTap, + }); @override @@ -294,14 +312,12 @@ class CustomExpandableTile extends StatefulWidget { } class _CustomExpandableTileState extends State { - // bool isExpanded = false; - late bool isExpanded; + bool isExpanded = false; + // late bool isExpanded; @override void initState() { - super.initState(); - isExpanded = - widget.isExpanded; // Initialize isExpanded based on widget's property + super.initState(); // Initialize isExpanded based on widget's property } @override @@ -312,11 +328,8 @@ class _CustomExpandableTileState extends State { child: Column( children: [ GestureDetector( - onTap: () { - setState(() { - isExpanded = !isExpanded; - }); - }, + onTap: () => widget.onTap(widget.index), + child: Container( decoration: BoxDecoration( color: widget.titleBackgroundColor, @@ -335,7 +348,7 @@ class _CustomExpandableTileState extends State { ), ), Icon( - isExpanded + widget.isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, color: Colors.white, @@ -350,8 +363,8 @@ class _CustomExpandableTileState extends State { duration: Duration(milliseconds: 300), curve: Curves.easeInOut, width: double.infinity, - height: isExpanded ? myheight * 0.52 : 0, - child: isExpanded + height: widget.isExpanded ? myheight * 0.52 : 0, + child: widget.isExpanded ? SingleChildScrollView( child: Container( decoration: BoxDecoration( diff --git a/lib/presentation/Screens/auth_verification/create_new_pw.dart b/lib/presentation/Screens/auth_verification/create_new_pw.dart index 1fa3a802..2e63faa1 100644 --- a/lib/presentation/Screens/auth_verification/create_new_pw.dart +++ b/lib/presentation/Screens/auth_verification/create_new_pw.dart @@ -205,7 +205,31 @@ class _CreateNewPwState extends State { padding: const EdgeInsets.all(24.0), child: Column( children: [ + // SizedBox(height: screenHeight / 7), + Align( + alignment: Alignment.topRight, + child: GestureDetector( + onTap: () { + context.go('/editProfile'); + }, + child: Container( + margin: EdgeInsets.only(top: 16,left: 16,bottom: 16,right: 1), // Add margin for positioning + width: 30, // Circle diameter + height: 30, + decoration: BoxDecoration( + color: Colors.grey[300], // Circle color + shape: BoxShape.circle, + ), + child: Icon( + Icons.close, + size: 20, // Icon size + color: Colors.white, // Icon color + ), + ), + ), + ), SizedBox(height: screenHeight / 6), + Text( // "Create New Password", AppLocalizations.of(context)!.create_new_password, diff --git a/lib/presentation/Screens/charts/screens/chart_screen.dart b/lib/presentation/Screens/charts/screens/chart_screen.dart index 3682dcb4..291e7e1d 100644 --- a/lib/presentation/Screens/charts/screens/chart_screen.dart +++ b/lib/presentation/Screens/charts/screens/chart_screen.dart @@ -1,15 +1,20 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/presentation/Screens/charts/services/api_service.dart'; import 'package:uae_stat/presentation/Screens/charts/widgets/chart_widget.dart'; +import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import '../filters/search_filter_helper.dart'; -class ChartScreen1 extends StatefulWidget { +class ChartScreen1 extends ConsumerStatefulWidget { final String dataSets; final String bgColor; final String mainTopic; final String title; + final String? keyParam; // Nullable String const ChartScreen1({ Key? key, @@ -17,18 +22,20 @@ class ChartScreen1 extends StatefulWidget { required String this.bgColor, required this.mainTopic, required this.title, + required this.keyParam, }); @override - _ChartScreen1State createState() => _ChartScreen1State(); + ConsumerState createState() => _ChartScreen1State(); } -class _ChartScreen1State extends State { +class _ChartScreen1State extends ConsumerState { List> selectedFiltersStorage = []; final GlobalKey _scaffoldKey = GlobalKey(); final ApiService apiService = ApiService(); bool isLoading = true; List isChartData = []; List nonChartData = []; + List filterDataSet = []; List filterData = []; List chartsData = []; List tabFilteredChartData = []; @@ -50,7 +57,8 @@ class _ChartScreen1State extends State { final String bgColor = widget.bgColor; print(' bgColor $bgColor'); // fetchChartData(widget.dataSets); - fetchChartData(widget.dataSets).then((_) { + final locale = ref.read(localeProvider); + fetchChartData(widget.dataSets, locale?.languageCode ?? 'en').then((_) { if (_tabsData.isNotEmpty) { // Call onTabSelected for the first tab onTabSelected(_tabsData[0]['id']); @@ -74,6 +82,7 @@ class _ChartScreen1State extends State { }); _scrollToIndex(_activeTabIndex); onTabSelected(_tabsData[_activeTabIndex]['id']!); // Pass the tab's id + print("TABFiltered Data: $filterData"); } } @@ -87,9 +96,39 @@ class _ChartScreen1State extends State { } } + // void processChartData(chartsData) { + // // Group data by 'kpi' + // Map>> groupedData = {}; + // for (var chart in chartsData) { + // String kpi = chart['kpi'] ?? ''; + // if (!groupedData.containsKey(kpi)) { + // groupedData[kpi] = []; + // } + // groupedData[kpi]!.add(chart); + // } + // + // // Format 'kpi' values for _tabs + // List> _tabs = groupedData.keys.map((kpi) { + // String name = kpi + // .split('_') // Split by underscore + // .map( + // (word) => word[0].toUpperCase() + word.substring(1)) // Capitalize + // .join(' '); // Join words with space + // + // return {'id': kpi, 'name': name}; + // }).toList(); + // + // print('Grouped Data: $groupedData'); + // print('Tabs: $_tabs'); + // setState(() { + // _tabsData = _tabs; + // }); + // } + void processChartData(chartsData) { // Group data by 'kpi' Map>> groupedData = {}; + for (var chart in chartsData) { String kpi = chart['kpi'] ?? ''; if (!groupedData.containsKey(kpi)) { @@ -98,30 +137,52 @@ class _ChartScreen1State extends State { groupedData[kpi]!.add(chart); } - // Format 'kpi' values for _tabs - List> _tabs = groupedData.keys.map((kpi) { - String name = kpi + // Format 'kpi' values for _tabs with tab_heading + List> _tabs = groupedData.entries.map((entry) { + String kpi = entry.key; + + // Extract tab_heading from the first chart in the grouped list + String tabHeading = entry.value.isNotEmpty + ? entry.value.first['tab_heading'] ?? 'Unknown' + : 'Unknown'; + + String formattedKpi = kpi .split('_') // Split by underscore - .map( - (word) => word[0].toUpperCase() + word.substring(1)) // Capitalize + .map((word) => word.isNotEmpty + ? word[0].toUpperCase() + word.substring(1) + : '') // Capitalize .join(' '); // Join words with space - return {'id': kpi, 'name': name}; + return {'id': kpi, 'name': tabHeading}; }).toList(); print('Grouped Data: $groupedData'); print('Tabs: $_tabs'); + setState(() { _tabsData = _tabs; }); } - Future fetchChartData(String dataSets) async { - var data = await apiService.fetchChartData(dataSets); + Future fetchChartData(String dataSets, locale) async { + var data = await apiService.fetchChartData(dataSets, locale); + + if (data.containsKey('filterData')) { + var filterData = data['filterData']; + filterDataSet = filterData.entries + .map((entry) => {'key': entry.key, 'value': entry.value}) + .toList(); + print("filterDataf1:- $filterData"); + print("filterDataf11:- $filterDataSet"); + } else { + filterDataSet = data['filterData'] ?? []; + print('filterData not found!'); + } + setState(() { isChartData = data['isChartData'] ?? []; nonChartData = data['nonChartData'] ?? []; - filterData = data['filterData'] ?? []; + // filterData = data['filterData'] ?? []; originalChartsData = List.from(isChartData); // Store original data originalCardData = List.from(nonChartData); @@ -133,17 +194,19 @@ class _ChartScreen1State extends State { print('chartsData :- $chartsData'); print('cardData :- $cardData'); - print('filterData :- $filterData'); + // print('filterData :- $filterData'); isLoading = false; }); - print(chartsData); - print(cardData); + print('chartsData -$chartsData'); + print('cardData -$cardData'); } void applyFilters(BuildContext context, List filters, List data, List dataCard, List selectedFilters) { - print("Selected Filters before applying: $selectedFilters"); - // Check if all filter_data is empty + print("applyFilters called"); + print("Selected ApplyFilters: $selectedFilters"); + print("Selected data: $data"); + if (selectedFilters.every((filter) => filter['filter_data'].isEmpty)) { setState(() { chartsData = @@ -157,8 +220,9 @@ class _ChartScreen1State extends State { // Loop through each chart data in the `data` list List filteredData = []; + Set addedChartIds = {}; // Track unique chart identifiers for (var chart in data) { - // Extract response data for filtering + final groupBy = chart['group_by']; List response = chart['response'] ?? []; // Filter the response based on selected filters @@ -171,20 +235,31 @@ class _ChartScreen1State extends State { 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)) { + if (groupBy == filterKey && obsKey.containsKey(filterKey)) { final obsKeyValue = obsKey[filterKey]?.toString(); return filterValues.isEmpty || filterValues.contains(obsKeyValue); } - return false; + + if (filterKey == 'TIME_PERIOD' && obsKey.containsKey('TIME_PERIOD')) { + final timePeriodValue = obsKey['TIME_PERIOD']?.toString(); + return filterValues.isEmpty || + filterValues.contains(timePeriodValue); + } + + return true; }); }).toList(); // If any data matches the filter, add the whole chart data object - if (chartFilteredData.isNotEmpty) { + // Add filtered chart only once + if (chartFilteredData.isNotEmpty && + !addedChartIds.contains(chart['chart_heading'])) { filteredData.add({ - ...chart, // Include all other properties of the chart object - 'response': chartFilteredData, // Only include filtered response data + ...chart, + 'response': chartFilteredData, }); + addedChartIds + .add(chart['chart_heading']); // Track by a unique identifier } } @@ -224,7 +299,7 @@ class _ChartScreen1State extends State { // Update the chartsData with the filtered data setState(() { chartsData = filteredData; - cardData = filteredCardData; + cardData = filteredCardData; // Adjust this part as needed }); print("Filtered Data: $filteredData"); @@ -271,7 +346,10 @@ class _ChartScreen1State extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Filters', + context.translate( + 'Filters', + 'المرشحات', + ), style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, @@ -357,7 +435,12 @@ class _ChartScreen1State extends State { setState(() {}); Navigator.pop(context); }, - child: Text('OK'), + child: Text( + context.translate( + 'OK', + 'نعم', + ), + ), ), ], ); @@ -428,7 +511,10 @@ class _ChartScreen1State extends State { selectedFiltersStorage.clear(); Navigator.pop(context); }, - child: Text('Clear'), + child: Text(context.translate( + 'Clear', + 'واضح', + )), style: ElevatedButton.styleFrom( backgroundColor: Colors.grey, ), @@ -447,7 +533,10 @@ class _ChartScreen1State extends State { // Save the selected filters to storage after applying selectedFiltersStorage = List.from(selectedFilters); }, - child: Text('Apply Filter'), + child: Text(context.translate( + 'Apply Filter', + 'تطبيق الفلتر', + )), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, ), @@ -466,6 +555,28 @@ class _ChartScreen1State extends State { void onTabSelected(String tabId) { print('Selected Tab: $tabId'); selectedFiltersStorage.clear(); + // Filter the filterDataSet + filterData = filterDataSet + .where((item) => item['key'] == tabId) + .map((item) => item['value']) + .where((item) => item != null && item is Iterable) + .expand((item) => item) + .toList(); + + for (var item in filterData) { + if (item['filter_key'] == 'TIME_PERIOD') { + // Convert the values to integers, sort them, and convert back to strings + List timePeriodData = item['filter_data'] + .map((e) => int.parse(e.toString())) // Convert to int + .toList(); + timePeriodData.sort((a, b) => a.compareTo(b)); // Sort numerically + + // Optionally, convert sorted integers back to strings if necessary + item['filter_data'] = timePeriodData.map((e) => e.toString()).toList(); + } + } + + print("TABFiltered Data: $filterData"); setState(() { chartsData = originalChartsData; cardData = originalCardData; @@ -507,22 +618,38 @@ class _ChartScreen1State extends State { // For example, you can update the chart data or display the results } - double calculateAspectRatio(int itemCount) { - // Modify the logic based on your layout requirements - if (itemCount <= 2) { - return 190.5 / 180; - } else if (itemCount == 3) { - return 180.0 / 300; - } else if (itemCount == 4) { - return 190.5 / 180; + // double calculateAspectRatio(int itemCount) { + // // Modify the logic based on your layout requirements + // if (itemCount <= 2) { + // return 190.5 / 180; + // } else if (itemCount == 3) { + // return 180.0 / 250; + // } else if (itemCount == 4) { + // return 190.5 / 180; + // } else { + // return 180.0 / 180; // Default for more items + // // return 190.5 / 180; + // } + // } + + double calculateAspectRatio(int crossAxisCount, List cardData) { + // Determine a default aspect ratio based on the most common chart type in the list + if (cardData.any((item) => + item['chart_type'] == 'total' || item['chart_type'] == 'average')) { + return crossAxisCount == 2 ? 1.0 : 0.7; // Larger Content + // return crossAxisCount == 2 ? 1.5 : 0.9; } else { - return 180.0 / 260; // Default for more items - // return 190.5 / 180; + return crossAxisCount == 2 ? 1.5 : 0.9; + // Shorter Content } } @override Widget build(BuildContext context) { + ref.listen(localeProvider, (previous, next) { + final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + fetchChartData(widget.dataSets, localeCode); + }); double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; final color = @@ -536,11 +663,18 @@ class _ChartScreen1State extends State { leading: IconButton( icon: Icon(Icons.arrow_back_ios_new, color: Colors.white), onPressed: () { - context.go('/uaenumbers'); + if (widget.keyParam == 'home') { + context.go('/myhomepage'); + } else { + context.go('/uaenumbers'); + } }, ), title: Text( - 'UAE Numbers', + context.translate( + 'UAE Numbers', + 'أرقام الإمارات', + ), style: TextStyle(color: Colors.white), ), ), @@ -609,7 +743,10 @@ class _ChartScreen1State extends State { Row( children: [ Text( - 'Bookmark', + context.translate( + 'Bookmark', + 'إشارة مرجعية', + ), style: const TextStyle( fontSize: 16, color: Colors.white, @@ -624,7 +761,10 @@ class _ChartScreen1State extends State { Row( children: [ Text( - 'Share', + context.translate( + 'Share', + 'يشارك', + ), style: const TextStyle( fontSize: 16, color: Colors.white, @@ -653,33 +793,37 @@ class _ChartScreen1State extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ // Left arrow button - _buildArrowButton( - onPressed: _activeTabIndex > 0 ? _scrollLeft : null, - icon: Icons.arrow_back_ios_new, - ), + if (_tabsData.length > 1) + _buildArrowButton( + onPressed: + _activeTabIndex > 0 ? _scrollLeft : null, + icon: Icons.arrow_back_ios_new, + ), // Tabs with horizontal scroll - Expanded( - child: SingleChildScrollView( - controller: _scrollController, - scrollDirection: Axis.horizontal, - child: Row( - children: - List.generate(_tabsData.length, (index) { - return _buildTab( - _tabsData[index], - isActive: index == _activeTabIndex, - ); - }), + if (_tabsData.length > 1) + Expanded( + child: SingleChildScrollView( + controller: _scrollController, + scrollDirection: Axis.horizontal, + child: Row( + children: + List.generate(_tabsData.length, (index) { + return _buildTab( + _tabsData[index], + isActive: index == _activeTabIndex, + ); + }), + ), ), ), - ), // Right arrow button - _buildArrowButton( - onPressed: _activeTabIndex < _tabsData.length - 1 - ? _scrollRight - : null, - icon: Icons.arrow_forward_ios, - ), + if (_tabsData.length > 1) + _buildArrowButton( + onPressed: _activeTabIndex < _tabsData.length - 1 + ? _scrollRight + : null, + icon: Icons.arrow_forward_ios, + ), ], ), // Expanded( @@ -699,25 +843,32 @@ class _ChartScreen1State extends State { 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 - crossAxisSpacing: 2, - mainAxisSpacing: 5, - childAspectRatio: - calculateAspectRatio(cardData.length)), + 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), + ), itemBuilder: (context, index) { final item = cardData[index]; print('item item item $item'); final chart_type = item['chart_type']; final chart_heading = item['chart_heading']; + final card_logo = item['card_logo']; final data = apiService.processNonChartData(item); print('processNonChartData'); + double cardHeight = (chart_type == 'totals' || + chart_type == 'averages') + ? 180.0 + : 130.0; + if (chart_type == 'total') { return Card( margin: const EdgeInsets.all(10), @@ -725,21 +876,41 @@ class _ChartScreen1State extends State { borderRadius: BorderRadius.circular(12), ), elevation: 4, - child: Padding( + 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), + // 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), - Text( - '${chart_heading ?? 'NA'}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 11, - fontWeight: FontWeight.w400, - color: Colors.black87, + Flexible( + fit: FlexFit.loose, + child: FittedBox( + // fit: BoxFit.contain, + child: Text( + '${chart_heading ?? 'NA'}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w400, + color: Colors.black87, + ), + ), ), ), Text( @@ -747,13 +918,19 @@ class _ChartScreen1State extends State { style: const TextStyle( fontSize: 11, color: Colors.grey), ), - Text( - apiService.formatAmount( - data['lastYearValue']), - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w900, - color: Color(0xFF90B0D5), + Flexible( + fit: FlexFit.loose, + child: FittedBox( + fit: BoxFit.contain, + child: Text( + apiService.formatAmount( + data['lastYearValue']), + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.w900, + color: Color(0xFF90B0D5), + ), + ), ), ), SizedBox( @@ -764,19 +941,27 @@ class _ChartScreen1State extends State { 1, // Divider line thickness ), ), - Text( - apiService.formatAmount( - data['secondLastYearValue']), - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFFD83731), + Flexible( + fit: FlexFit.loose, + child: FittedBox( + fit: BoxFit.contain, + child: Text( + apiService.formatAmount( + data['secondLastYearValue']), + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color(0xFFD83731), + ), + ), ), ), Text( '(${data['secondLastYear'] ?? 'NA'})', style: const TextStyle( - fontSize: 11, color: Colors.grey), + fontSize: 8, + fontWeight: FontWeight.w500, + color: Colors.grey), ), ], ), @@ -789,13 +974,24 @@ class _ChartScreen1State extends State { borderRadius: BorderRadius.circular(12), ), elevation: 4, - child: Padding( + child: Container( + height: cardHeight, padding: const EdgeInsets.all(16.0), child: Column( + mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.center, children: [ - const Icon(Icons.analytics, - 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: 5), Text( '${chart_heading ?? 'NA'}', @@ -811,12 +1007,133 @@ class _ChartScreen1State extends State { style: const TextStyle( fontSize: 11, color: Colors.grey), ), + Flexible( + fit: FlexFit.loose, + child: FittedBox( + fit: BoxFit.contain, + child: Text( + '${data['roundedAverage'] ?? 'NA'}', + style: const TextStyle( + fontSize: 26, + fontWeight: FontWeight.w900, + color: Color(0xFF90B0D5), + ), + ), + ), + ), + ], + ), + ), + ); + } else if (chart_type == 'totals') { + return Card( + margin: const EdgeInsets.all(10), + 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( + // fit: BoxFit.contain, + child: Text( + '${chart_heading ?? 'NA'}', + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 11, + fontWeight: FontWeight.w400, + color: Colors.black87, + ), + ), + ), + ), Text( - '${data['roundedAverage'] ?? 'NA'}', + '(${data['lastYear'] ?? 'NA'})', style: const TextStyle( - fontSize: 22, - fontWeight: FontWeight.w900, - color: Color(0xFF90B0D5), + 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 Card( + margin: const EdgeInsets.all(10), + 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( + fit: BoxFit.contain, + child: Text( + apiService.formatAmount( + data['lastYearValue']), + style: const TextStyle( + fontSize: 22, + fontWeight: FontWeight.w900, + color: Color(0xFF90B0D5), + ), + ), ), ), ], diff --git a/lib/presentation/Screens/charts/services/api_service.dart b/lib/presentation/Screens/charts/services/api_service.dart index 31494a16..a99ecb50 100644 --- a/lib/presentation/Screens/charts/services/api_service.dart +++ b/lib/presentation/Screens/charts/services/api_service.dart @@ -13,13 +13,13 @@ class ChartData { class ApiService { static const String baseUrl = 'https://pb.venbait.in/api/getDataSet'; - Future> fetchChartData(String dataSets) async { + Future> fetchChartData(String dataSets, locale) async { List isChartData = []; List nonChartData = []; List originalChartsData = []; List originalCardData = []; - final url = Uri.parse('$baseUrl?dataset=$dataSets'); + final url = Uri.parse('$baseUrl?dataset=$dataSets&language=$locale'); try { final response = await http.get(url); @@ -43,7 +43,8 @@ class ApiService { return { 'isChartData': isChartData, 'nonChartData': nonChartData, - 'filterData': jsonData['filter_data'] + 'filterData': jsonData['new_filter_data'] + // 'filterData': jsonData['filter_data'] // 'originalChartsData': originalChartsData, // 'originalCardData': originalCardData, }; diff --git a/lib/presentation/Screens/charts/widgets/chart_widget.dart b/lib/presentation/Screens/charts/widgets/chart_widget.dart index 5e24182b..7451e2a9 100644 --- a/lib/presentation/Screens/charts/widgets/chart_widget.dart +++ b/lib/presentation/Screens/charts/widgets/chart_widget.dart @@ -294,7 +294,7 @@ class ChartWidget extends StatelessWidget { aspectRatio: 1.5, child: BarChart( BarChartData( - alignment: BarChartAlignment.start, + alignment: BarChartAlignment.spaceEvenly, barTouchData: BarTouchData( touchTooltipData: BarTouchTooltipData( // tooltipBgColor: Colors.black.withOpacity(0.8), @@ -324,6 +324,17 @@ class ChartWidget extends StatelessWidget { double value = toY - fromY; String groupName = groupNames[i]; + 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 + } // Get the color for the current group Color groupColor = _getColorForGroup( i); // Replace with your color logic @@ -337,8 +348,7 @@ class ChartWidget extends StatelessWidget { fontSize: 14), // Circle color ), TextSpan( - text: - '$groupName - ${value.toStringAsFixed(0)}\n', + text: '$groupName - $formattedValue\n', style: TextStyle(color: Colors.white, fontSize: 12), ), @@ -433,147 +443,618 @@ class ChartWidget extends StatelessWidget { ], ); case 'line_trend': - case 'line_trend': - return LineChart(LineChartData( - lineTouchData: lineTouchData1(), - gridData: gridData(), - titlesData: titlesData1(), - borderData: borderData(), - lineBarsData: lineBarsData(chartData), - minX: 1970, // Adjust based on your data range - maxX: 2025, // Adjust based on your data range - )); - case 'fl_multi_bar': - return BarChart( - BarChartData( - alignment: BarChartAlignment.spaceAround, - maxY: 100, // Maximum value for the bar - barGroups: _buildBarGroups(), - titlesData: FlTitlesData( - leftTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - '${value.toInt()}%', - style: TextStyle(fontSize: 12), - ); - }, - ), - ), - bottomTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - getTitlesWidget: (value, meta) { - return Text( - "Bar ${value.toInt()}", - style: TextStyle(fontSize: 12), - ); - }, - ), - ), - topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), - rightTitles: - AxisTitles(sideTitles: SideTitles(showTitles: false)), + final List uniqueColorsLine_trend_2 = [ + Color(0xFF6097CD), + Color(0xFFD086A7), + Color(0xFF98BCE5), + Color(0xFFA7B5C5), + Color(0xFFBED3EC), + Color(0xFFD4E3F4), + ]; + Map groupColorMap = {}; + int colorIndex = 0; + for (String group in groupByValues) { + groupColorMap[group] = uniqueColorsLine_trend_2[ + colorIndex % uniqueColorsLine_trend_2.length]; + colorIndex++; + } + + // Extract all years from the chart data + List years = (chartData['response'] as List) + .map((entry) => + int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0) + .toList(); + + if (years.isEmpty) { + // Return an empty chart if no data + return LineChart( + LineChartData( + titlesData: FlTitlesData(show: false), + lineBarsData: [], ), - borderData: FlBorderData( - show: false, + ); + } + + // Find the maximum year and calculate the range for the last 5 years + int maxYear = years.reduce((a, b) => a > b ? a : b); + int minYear = maxYear - 5; + + // Filter chart data to only include entries within the last 5 years + List filteredData = + (chartData['response'] as List).where((entry) { + int year = int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0; + return year >= minYear && year <= maxYear; + }).toList(); + + Set uniqueXValues = filteredData + .map( + (entry) => double.parse(entry['ObsKey']['TIME_PERIOD'])) + .toSet(); + + // Generate line bars for the chart + List lineBars = + lineBarsData(filteredData, groupByValues, groupByKey); + + return Column(children: [ + Text( + chartData['chart_heading'] ?? '', // Chart title from data + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, ), - barTouchData: BarTouchData(enabled: true), - gridData: FlGridData(show: false), + textAlign: TextAlign.center, ), - ); + SizedBox(height: 10), + // Chart + Expanded( + child: LineChart(LineChartData( + lineTouchData: lineTouchData1(), + gridData: gridData(), + titlesData: titlesData1(uniqueXValues), + borderData: borderData(), + lineBarsData: lineBars, + minX: uniqueXValues.reduce((a, b) => a < b ? a : b), + maxX: uniqueXValues.reduce((a, b) => a > b ? a : b), + ))), + Padding( + padding: const EdgeInsets.all(8.0), + child: Wrap( + spacing: 12, + runSpacing: 8, + children: groupByValues.map((group) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 12, + height: 12, + color: groupColorMap[group], + ), + SizedBox(width: 6), + Text( + group, + style: TextStyle(fontSize: 14), + ), + ], + ); + }).toList(), + ), + ), + ]); + case 'line_trend_2': + Map groupColorMap = {}; + int colorIndex = 0; + for (String group in groupByValues) { + groupColorMap[group] = uniqueColors[colorIndex % uniqueColors.length]; + colorIndex++; + } + + // Extract all years from the chart data + List years = (chartData['response'] as List) + .map((entry) => + int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0) + .toList(); + + if (years.isEmpty) { + // Return an empty chart if no data + return LineChart( + LineChartData( + titlesData: FlTitlesData(show: false), + lineBarsData: [], + ), + ); + } + + // Find the maximum year and calculate the range for the last 5 years + int maxYear = years.reduce((a, b) => a > b ? a : b); + int minYear = maxYear - 5; + + // Filter chart data to only include entries within the last 5 years + List filteredData = + (chartData['response'] as List).where((entry) { + int year = int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0; + return year >= minYear && year <= maxYear; + }).toList(); + + Set uniqueXValues = filteredData + .map( + (entry) => double.parse(entry['ObsKey']['TIME_PERIOD'])) + .toSet(); + + // Generate line bars for the chart + List lineBars = + lineBarsData2(filteredData, groupByValues, groupByKey); + + return Column(children: [ + Text( + chartData['chart_heading'] ?? '', // Chart title from data + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 10), + // Chart + Expanded( + child: LineChart(LineChartData( + lineTouchData: lineTouchData1(), + gridData: gridData(), + titlesData: titlesData2(uniqueXValues), + borderData: borderData(), + lineBarsData: lineBars, + minX: uniqueXValues.reduce((a, b) => a < b ? a : b), + maxX: uniqueXValues.reduce((a, b) => a > b ? a : b), + ))), + ]); + case 'bar_chart': + // Extract groupBy values and their corresponding y-axis values + List xAxisData = []; + List yAxisData = []; + + for (var entry in chartData['response']) { + var xValue = entry['ObsKey'][groupByKey]; + var yValue = entry['ObsValue']['Value']; + if (xValue != null && yValue != null) { + xAxisData.add(xValue.toString()); + yAxisData.add(double.tryParse(yValue.toString()) ?? 0.0); + } + } + return Column(children: [ + Text( + chartData['chart_heading'] ?? '', // Chart title from data + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 10), + // Chart + Expanded( + child: BarChart( + BarChartData( + alignment: BarChartAlignment.spaceAround, + maxY: yAxisData.isNotEmpty + ? yAxisData.reduce((a, b) => a > b ? a : b) * 1.2 + : 10, + barTouchData: BarTouchData(enabled: true), + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: false, + interval: (yAxisData.isNotEmpty + ? yAxisData.reduce((a, b) => a > b ? a : b) / 5 + : 1), + getTitlesWidget: (value, meta) { + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Text('${value.toInt()}'), + ); + }, + reservedSize: 60, + ), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + if (value.toInt() < xAxisData.length) { + return Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Text(xAxisData[value.toInt()]), + ); + } + return Container(); + }, + reservedSize: 40, + ), + ), + topTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hide top titles + ), + rightTitles: AxisTitles( + sideTitles: + SideTitles(showTitles: false), // Hide right titles + ), + ), + gridData: FlGridData(show: false), + borderData: FlBorderData(show: false), + barGroups: List.generate( + xAxisData.length, + (index) => BarChartGroupData( + x: index, + barRods: [ + BarChartRodData( + toY: yAxisData[index], + color: Colors.blueAccent, + borderRadius: BorderRadius.circular(4), + width: 20, + ), + ], + ), + ), + ), + )) + ]); + case 'fl_multi_bar': + double _calculateChartWidth(dynamic chartData) { + int totalBars = chartData['response']?.length ?? 0; + const double barWidth = 30; // Width for each bar, including spacing + return totalBars * barWidth; // Calculate total chart width + } + // 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 { + crop = item['ObsKey']['CROP']; + cropType = item['ObsKey'][groupByKey]; + } + + if (groupedCrops.containsKey(cropType)) { + groupedCrops[cropType]!.add(crop); + } else { + groupedCrops[cropType] = [crop]; + } + } + return Column(children: [ + Text( + chartData['chart_heading'] ?? '', // Chart title from data + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 10), + // Chart + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, // Enable horizontal scrolling + child: SizedBox( + width: _calculateChartWidth( + chartData), // Dynamically calculate the chart width + child: BarChart( + BarChartData( + alignment: BarChartAlignment.spaceAround, + maxY: + _calculateMaxY(chartData), // Dynamically calculate max Y + barGroups: _buildHorizontalRotateBarGroups( + chartData, groupByValues), // Build bar groups + titlesData: FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 20, // Added space for rotated titles + getTitlesWidget: (value, meta) { + if (value < groupByValues.length) { + String title = + groupByValues.elementAt(value.toInt()); + return Transform.rotate( + angle: + -0.5, // Rotation in radians (~ -30 degrees) + child: Text( + title, + style: const TextStyle(fontSize: 12), + ), + ); + } + return const SizedBox.shrink(); + }, + ), + ), + topTitles: + AxisTitles(sideTitles: SideTitles(showTitles: false)), + rightTitles: + AxisTitles(sideTitles: SideTitles(showTitles: false)), + ), + borderData: FlBorderData(show: false), + barTouchData: BarTouchData( + touchTooltipData: BarTouchTooltipData( + tooltipHorizontalAlignment: FLHorizontalAlignment.center, + // 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) { + // Get the group label dynamically + String groupLabel = + groupByValues.elementAt(groupIndex); + + // Fetch the crop for the current group from groupedCrops + String cropType = groupByValues.elementAt(groupIndex); + print('GrpcropType: $cropType'); + // String crop = groupedCrops[cropType]![rodIndex]; + + // Safely fetch crop with null check + List? crops = groupedCrops[cropType]; + String crop; + if (crops != null && rodIndex < crops.length) { + crop = crops[rodIndex]; + } else { + print( + 'Crop is null or index out of bounds for cropType: $cropType and rodIndex: $rodIndex'); + crop = cropType; // Fallback to cropType + } + + // print('groupedCrops1: $groupedCrops - $rodIndex'); + // print( + // 'Groupcrop: $crop'); + 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 + } + // print('Cropvaluevalue: $groupLabel\n$crop $value $formattedValue'); + + return BarTooltipItem( + '$groupLabel\n$crop', + const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + children: [ + TextSpan( + // text: ' Value: ${rod.toY}', + 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 + // }); + } + }, + ), + gridData: FlGridData(show: false), + ), + ), + ), + )) + ]); + + case 'horizontal_rotate': + // 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 = item['ObsKey']['CROP']; + String cropType = item['ObsKey']['CROP_TYPE']; + + if (groupedCrops.containsKey(cropType)) { + groupedCrops[cropType]!.add(crop); + } else { + groupedCrops[cropType] = [crop]; + } + } + int touchedGroupIndex = -1; + + int rotationTurns = 1; + print('Grouped Crops: $groupedCrops'); + return Column(children: [ + Text( + chartData['chart_heading'] ?? '', // Chart title from data + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + ), + textAlign: TextAlign.center, + ), + SizedBox(height: 10), + // Chart + Expanded( + child: BarChart( + BarChartData( + maxY: 400000, + rotationQuarterTurns: + rotationTurns, // Adjust maxY as needed based on your data + barTouchData: BarTouchData( + touchTooltipData: BarTouchTooltipData( + tooltipHorizontalAlignment: FLHorizontalAlignment.center, + // 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'); + + // 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; + + 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 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: 20, // Added space for rotated titles + getTitlesWidget: (value, meta) { + if (value < groupByValues.length) { + String title = groupByValues.elementAt(value.toInt()); + return Transform.rotate( + angle: -0.5, // Rotation in radians (~ -30 degrees) + child: Text( + title, + style: const TextStyle(fontSize: 12), + ), + ); + } + 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.grey), + ), + ), + 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, + ), + )) + ]); default: return Center(child: Text('Unknown chart type')); } } -// Function to create LineChartBarData for Males and Females - List lineBarsData(dynamic chartData) { - List lineBars = []; - - // Filter data for males and females - List> maleData = []; - List> femaleData = []; - - // Separate the data based on gender - for (var entry in chartData['response']) { - if (entry['ObsKey']['GENDER'] == 'M') { - maleData.add(entry); - } else if (entry['ObsKey']['GENDER'] == 'F') { - femaleData.add(entry); - } - } - - // Prepare data for Male - List maleSpots = maleData.map((entry) { - double xValue = double.parse(entry['ObsKey']['TIME_PERIOD']); - double yValue = double.parse(entry['ObsValue']['Value']); - return FlSpot(xValue, yValue); - }).toList(); - - // Prepare data for Female - List femaleSpots = femaleData.map((entry) { - double xValue = double.parse(entry['ObsKey']['TIME_PERIOD']); - double yValue = double.parse(entry['ObsValue']['Value']); - return FlSpot(xValue, yValue); - }).toList(); - - // Add the Male line (Blue color) - lineBars.add( - LineChartBarData( - spots: maleSpots, - isCurved: true, - color: Colors.blue, - barWidth: 3, - belowBarData: BarAreaData(show: false), - ), - ); - - // Add the Female line (Pink color) - lineBars.add( - LineChartBarData( - spots: femaleSpots, - isCurved: true, - color: Colors.pink, - barWidth: 3, - belowBarData: BarAreaData(show: false), - ), - ); - - return lineBars; - } - -// Sample implementation for other chart details like titles, grid, etc. - LineTouchData lineTouchData1() { - return LineTouchData( - touchTooltipData: LineTouchTooltipData( - // tooltipBgColor: Colors.blueAccent, - ), - // touchCallback: (LineTouchResponse touchResponse) {}, - handleBuiltInTouches: true, - ); - } - - FlGridData gridData() { - return FlGridData( - show: true, - drawVerticalLine: true, - getDrawingHorizontalLine: (value) => - FlLine(color: Colors.grey, strokeWidth: 1), - getDrawingVerticalLine: (value) => - FlLine(color: Colors.grey, strokeWidth: 1), - ); - } - - FlTitlesData titlesData1() { +// Start Of line trend chart + FlTitlesData titlesData2(Set xValues) { return FlTitlesData( leftTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, + reservedSize: 40, getTitlesWidget: (value, meta) { + // Format values as millions (M) + // String formattedValue = (value / 1000000).toStringAsFixed(1) + 'M'; return Text( - value.toInt().toString(), + value.toStringAsFixed(1), style: TextStyle(color: Colors.black, fontSize: 12), ); }, @@ -582,22 +1063,244 @@ class ChartWidget extends StatelessWidget { bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, + interval: 1, // Ensure each year is shown only once getTitlesWidget: (value, meta) { - return Text( - value.toInt().toString(), - style: TextStyle(color: Colors.black, fontSize: 12), - ); + if (xValues.contains(value)) { + return Text( + value.toInt().toString(), + style: TextStyle(color: Colors.black, fontSize: 12), + ); + } else { + return SizedBox.shrink(); // Hide non-relevant labels + } }, ), ), + topTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hide top titles + ), + rightTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hide right titles + ), + ); + } + + List lineBarsData2(List filteredData, + Set groupByValues, String groupByKey) { + List lineBars = []; + print('filteredData $filteredData'); + print('filteredData222 $groupByValues'); + final List uniqueColors = [ + Color(0xFF648CBA), + Color(0xFF90B0D5), + Color(0xFF98BCE5), + Color(0xFFA7B5C5), + Color(0xFFBED3EC), + Color(0xFFD4E3F4), + ]; + + // Create a map to assign colors to each group in order + Map groupColorMap = {}; + int colorIndex = 0; + for (String group in groupByValues) { + groupColorMap[group] = uniqueColors[colorIndex % uniqueColors.length]; + colorIndex++; + } + + print('Group-Color Map: $groupColorMap'); + + // Create a map to store the values by year and gender (M and F) + Map> yearGenderMap = {}; + + // Populate the yearGenderMap with male and female values + for (var entry in filteredData) { + int year = int.parse(entry['ObsKey']['TIME_PERIOD']); + String gender = entry['ObsKey']['GENDER']; + double value = double.parse(entry['ObsValue']['Value'].toString()); + + if (!yearGenderMap.containsKey(year)) { + yearGenderMap[year] = {'M': 0.0, 'F': 0.0}; + } + + // Assign value based on gender + if (gender == 'M') { + yearGenderMap[year]!['M'] = value; + } else if (gender == 'F') { + yearGenderMap[year]!['F'] = value; + } + } + + // Calculate the ratio (M/F) * 100 for each year + List 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)); + } + }); + + // Add a line for the ratio data + lineBars.add( + LineChartBarData( + spots: spots, + isCurved: true, + color: Colors.blue, // Set color for the ratio line + barWidth: 3, + isStrokeCapRound: true, + belowBarData: BarAreaData(show: true), + ), + ); + + return lineBars; + } + + List lineBarsData(List filteredData, + Set groupByValues, String groupByKey) { + List lineBars = []; + print('filteredData $filteredData'); + print('filteredData222 $groupByValues'); + final List uniqueColors = [ + Color(0xFF6097CD), + Color(0xFFD086A7), + Color(0xFF98BCE5), + Color(0xFFA7B5C5), + Color(0xFFBED3EC), + Color(0xFFD4E3F4), + ]; + + // Create a map to assign colors to each group in order + Map groupColorMap = {}; + int colorIndex = 0; + for (String group in groupByValues) { + groupColorMap[group] = uniqueColors[colorIndex % uniqueColors.length]; + colorIndex++; + } + + print('Group-Color Map: $groupColorMap'); + // Iterate through each group and generate line data + for (String group in groupByValues) { + List spots = filteredData + .where((entry) => entry['ObsKey'][groupByKey] == group) + .map((entry) { + double xValue = double.parse(entry['ObsKey']['TIME_PERIOD']); + double yValue = double.parse(entry['ObsValue']['Value']); + return FlSpot(xValue, yValue); + }).toList(); + + // Add a line for this group + lineBars.add( + LineChartBarData( + spots: spots, + isCurved: true, + color: groupColorMap[group], + barWidth: 3, + isStrokeCapRound: true, + belowBarData: BarAreaData(show: false), + ), + ); + } + + return lineBars; + } + +// Helper functions for chart styles + LineTouchData lineTouchData1() { + return LineTouchData( + touchTooltipData: LineTouchTooltipData(), + handleBuiltInTouches: true, + ); + } + + FlGridData gridData() { + return FlGridData( + show: false, + drawVerticalLine: true, + getDrawingHorizontalLine: (value) => + FlLine(color: Colors.grey, strokeWidth: 1), + getDrawingVerticalLine: (value) => + FlLine(color: Colors.grey, strokeWidth: 1), + ); + } + + FlTitlesData titlesData1(Set xValues) { + return FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: 40, + getTitlesWidget: (value, meta) { + // Format values as millions (M) + String formattedValue; + + if (value % 10 == 0) { +// Check if the value is in the millions or thousands range + if (value >= 1000000) { + formattedValue = '${(value / 1000000).toStringAsFixed(0)}M'; + } else if (value >= 1000) { + formattedValue = '${(value / 1000).toStringAsFixed(0)}k'; + } else { + formattedValue = value.toStringAsFixed( + 0); // No decimals for values less than 1000 + } + + // Skip rendering if the formatted value is the same as the last one + if (_lastFormattedValue == formattedValue) { + return const SizedBox.shrink(); // Empty widget for duplicates + } + // Update the last formatted value for the next comparison + _lastFormattedValue = formattedValue; + + print('formattedValueLine - $formattedValue'); + return Text(formattedValue, style: TextStyle(fontSize: 10)); + } + return const SizedBox.shrink(); + + // return Text( + // formattedValue, + // style: TextStyle(color: Colors.black, fontSize: 12), + // ); + }, + ), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + interval: 1, // Ensure each year is shown only once + getTitlesWidget: (value, meta) { + if (xValues.contains(value)) { + return Text( + value.toInt().toString(), + style: TextStyle(color: Colors.black, fontSize: 12), + ); + } else { + return SizedBox.shrink(); // Hide non-relevant labels + } + }, + ), + ), + topTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hide top titles + ), + rightTitles: AxisTitles( + sideTitles: SideTitles(showTitles: false), // Hide right titles + ), ); } FlBorderData borderData() { return FlBorderData( - show: true, border: Border.all(color: Colors.black, width: 1)); + show: true, + border: Border( + bottom: BorderSide(color: Colors.black, width: 1), // Show bottom border + left: BorderSide(color: Colors.black, width: 1), // Show left border + top: BorderSide.none, // Hide top border + right: BorderSide.none, // Hide right border + ), + ); } + // End Of line trend chart + // Define the list of unique colors final List uniqueColors = [ Color(0xFF648CBA), @@ -642,7 +1345,7 @@ class ChartWidget extends StatelessWidget { List timePeriods = groupedData.values.first.keys.toList(); print('timePeriods $timePeriods'); - double barWidth = calculateBarWidth(context, timePeriods.length); + // double barWidth = calculateBarWidth(context, timePeriods.length); // For each time period, generate a BarChartGroupData for (int i = 0; i < timePeriods.length; i++) { @@ -678,8 +1381,8 @@ class ChartWidget extends StatelessWidget { BarChartRodData( toY: currentToY, // Use the accumulated `currentToY` value rodStackItems: rodStackItems, // Add the stacked items - // width: 20, - width: barWidth, + width: 20, + // width: barWidth, borderRadius: BorderRadius.zero, color: Colors .transparent, // This will act as a container for stacked items @@ -708,64 +1411,149 @@ class ChartWidget extends StatelessWidget { } // Function to dynamically set left axis titles (values) + String? _lastFormattedValue; Widget _generateLeftTitles(double value, TitleMeta meta) { + String formattedValue; + if (value % 1000 == 0) { - return Text(value.toStringAsFixed(0), style: TextStyle(fontSize: 10)); +// Check if the value is in the millions or thousands range + if (value >= 1000000) { + formattedValue = '${(value / 1000000).toStringAsFixed(0)}M'; + } else if (value >= 1000) { + formattedValue = '${(value / 1000).toStringAsFixed(0)}k'; + } else { + formattedValue = + value.toStringAsFixed(0); // No decimals for values less than 1000 + } + + // Skip rendering if the formatted value is the same as the last one + if (_lastFormattedValue == formattedValue) { + return const SizedBox.shrink(); // Empty widget for duplicates + } + // Update the last formatted value for the next comparison + _lastFormattedValue = formattedValue; + + print('formattedValue - $formattedValue'); + return Text(formattedValue, style: TextStyle(fontSize: 10)); } - return Container(); + return const SizedBox.shrink(); + // return Container(); } - /// Build the data for the bar groups - List _buildBarGroups() { - return [ - BarChartGroupData( - x: 1, // Identifier for the group - barRods: [ - BarChartRodData( - toY: 40, // Value for the first part of the stack - color: Colors.blue, - width: 10, - ), - BarChartRodData( - toY: 60, // Value for the second part of the stack - color: Colors.green, - width: 10, - ), - ], - ), - BarChartGroupData( - x: 2, - barRods: [ - BarChartRodData( - toY: 30, - color: Colors.orange, - width: 10, - ), - BarChartRodData( - toY: 70, - color: Colors.red, - width: 10, - ), - ], - ), - BarChartGroupData( - x: 3, - barRods: [ - BarChartRodData( - toY: 50, - color: Colors.purple, - width: 10, - ), - BarChartRodData( - toY: 50, - color: Colors.cyan, - width: 10, - ), - ], - ), - ]; + /// Start horizontal rotate and fl_multi_bar bar + // List _buildBarGroups(dynamic chartData) { + // String groupByKey = chartData['group_by']; + // if (groupByKey == null || chartData['response'] == null) { + // return []; + // } + // + // // Extract unique group_by values + // Set groupByValues = extractGroupByValues(chartData, groupByKey); + // + // // Create a map to hold grouped data + // Map>> groupedData = {}; + // for (var entry in chartData['response']) { + // String groupValue = entry['ObsKey'][groupByKey] ?? ''; + // if (groupValue.isNotEmpty) { + // groupedData.putIfAbsent(groupValue, () => []).add(entry); + // } + // } + // + // // Convert grouped data into BarChartGroupData + // List barGroups = []; + // int groupIndex = 0; + // + // groupedData.forEach((key, values) { + // int colorIndex = 0; // Track the color for each bar within the group + // + // List rods = values.map((entry) { + // double yValue = double.tryParse(entry['ObsValue']['Value'] ?? '0') ?? 0; + // + // // Cycle through colors for each bar + // final barColor = uniqueColors[colorIndex % uniqueColors.length]; + // colorIndex++; + // + // return BarChartRodData( + // fromY: 0, + // toY: yValue, + // color: barColor, + // width: 20, // Adjust bar width for better visibility + // borderRadius: BorderRadius.circular(4), + // ); + // }).toList(); + // + // barGroups.add(BarChartGroupData( + // x: groupIndex, + // barRods: rods, + // showingTooltipIndicators: [0], + // )); + // groupIndex++; + // }); + // + // return barGroups; + // } + + double _calculateMaxY(dynamic chartData) { + // Dynamically calculate the maximum Y value + List response = chartData['response'] ?? []; + double maxY = 0; + + for (var entry in response) { + double value = double.tryParse(entry['ObsValue']['Value'] ?? '0') ?? 0; + if (value > maxY) { + maxY = value; + } + } + + return maxY * 1.1; // Add 10% buffer for better visualization } + List _buildHorizontalRotateBarGroups( + dynamic chartData, Set groupByValues) { + List responseData = chartData['response']; + List barGroups = []; + int colorIndex = 0; + // Iterate through groupByValues and populate BarChartGroupData + for (int i = 0; i < groupByValues.length; i++) { + String groupValue = groupByValues.elementAt(i); + + // Filter data for the current group (grouping by CROP_TYPE) + List groupData = responseData.where((entry) { + return entry['ObsKey'][chartData['group_by']] == groupValue; + }).toList(); + + // Create BarChartRodData for each bar in the group + List barRods = groupData.map((data) { + // Ensure to retrieve and parse 'ObsValue' value (which should be a double) + double value = + double.tryParse(data['ObsValue']['Value'].toString()) ?? 0.0; + + final barColor = uniqueColors[colorIndex % uniqueColors.length]; + colorIndex++; + + return BarChartRodData( + toY: value, // Use the parsed value + color: barColor, // Dynamic color + width: 20, + ); + }).toList(); + + // Add BarChartGroupData for the group + barGroups.add( + BarChartGroupData( + x: i, + barRods: barRods, + showingTooltipIndicators: + List.generate(barRods.length, (index) => index), + ), + ); + } + + return barGroups; + } + + /// End horizontal rotate and fl_multi_bar bar + @override Widget build(BuildContext context) { return buildChart(chartData, context); diff --git a/lib/presentation/routes/auth_routes/login_route.dart b/lib/presentation/routes/auth_routes/login_route.dart index 29b2a978..20fb5f46 100644 --- a/lib/presentation/routes/auth_routes/login_route.dart +++ b/lib/presentation/routes/auth_routes/login_route.dart @@ -461,8 +461,16 @@ class LoginRoute extends HookConsumerWidget { final continueAsGuestBtn = SizedBox( width: double.infinity, child: ElevatedButton( - onPressed: () => - context.go('/myhomepage'), + onPressed: () async { + final prefs = await SharedPreferences.getInstance(); + prefs.clear(); + final userId = 'guest'; + if (userId.isNotEmpty) { + await saveUserId(userId); + } + context.go('/myhomepage'); + //context.go('/${context.language}/${BottomNavBarItem.home.routePath}'), + }, //context.go('/${context.language}/${BottomNavBarItem.home.routePath}'), style: ButtonStyle( shape: WidgetStatePropertyAll( diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart index 260f7692..4c41728f 100644 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart @@ -163,43 +163,21 @@ import 'dart:convert'; import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import '../../drawer_routes/custom_drawer_routes.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:http/http.dart' as http; -class MyHomePage extends StatefulWidget { +class MyHomePage extends ConsumerStatefulWidget { const MyHomePage({super.key}); @override - State createState() => _MyHomePageState(); + ConsumerState createState() => _MyHomePageState(); } -void handleInfoCardClick(BuildContext context, String data, Color color) { - // Handle navigation and pass dynamic data - print(data); - final dataSets = data; - if (dataSets != null) { - // Perform navigation - context.go('/chartScreen/$dataSets'); - } else { - // Show error if dataSet is null - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('No dataset available')), - ); - } - // final dataSets = data; - // print(dataSets); - // if (dataSets == 'hotels') { - // context.go('/chartScreen/$dataSets'); - // } else if (dataSets == 'divorces') { - // context.go('/chartScreen/$dataSets'); - // } else if (dataSets == 'marriages') { - // context.go('/Chart/$dataSets'); - // } -} - -class _MyHomePageState extends State { +class _MyHomePageState extends ConsumerState { @override Widget build(BuildContext context) { double myheight = MediaQuery.of(context).size.height; @@ -215,14 +193,14 @@ class _MyHomePageState extends State { } } -class EconomyStatsWidget extends StatefulWidget { +class EconomyStatsWidget extends ConsumerStatefulWidget { const EconomyStatsWidget({super.key}); @override - EconomyStatsState createState() => EconomyStatsState(); + ConsumerState createState() => EconomyStatsState(); } -class EconomyStatsState extends State { +class EconomyStatsState extends ConsumerState { List data = []; bool isLoading = true; @@ -230,13 +208,22 @@ class EconomyStatsState extends State { @override void initState() { super.initState(); - fetchData(); + final locale = ref.read(localeProvider); + fetchData(locale?.languageCode ?? 'en'); } - Future fetchData() async { + // @override + // void didChangeDependencies() { + // super.didChangeDependencies(); + // // Access the provider here + // final locale = ref.watch(localeProvider); + // fetchData(locale); // Pass the locale to the fetchData method + // } + + Future fetchData(locale) async { const baseUrl = 'https://pb.venbait.in/api/getHomePageData'; try { - final response = await http.get(Uri.parse(baseUrl)); + final response = await http.get(Uri.parse(baseUrl + '?language=$locale')); if (response.statusCode == 200) { setState(() { data = json.decode(response.body); @@ -283,6 +270,10 @@ class EconomyStatsState extends State { } Widget build(BuildContext context) { + ref.listen(localeProvider, (previous, next) { + final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null + fetchData(localeCode); + }); double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; @@ -386,7 +377,8 @@ class EconomyStatsState extends State { children: _buildRows( [tileData], borderColor, - mainTopic['color_pattern']), + mainTopic['color_pattern'], + mainTopic['main_topic']), ), ), ], @@ -452,8 +444,8 @@ class EconomyStatsState extends State { // return rows; // } -List _buildRows( - List> tileData, Color borderColor, color_pattern) { +List _buildRows(List> tileData, Color borderColor, + String colorPattern, String mainTopic) { // tileData.sort((a, b) => // (a['data_set_list_order'] ?? 0).compareTo(b['data_set_list_order'] ?? 0),); @@ -479,8 +471,10 @@ List _buildRows( dataset: tile['data_set']!, bordercolor: borderColor, textcolor: borderColor, + colorPattern: colorPattern, backgroundColor: Colors.white, // Set background color for InfoCard + mainTopic: mainTopic, onTap: () {}, ), ); @@ -529,10 +523,12 @@ class RoundedCornerContainer extends StatelessWidget { class InfoCard extends StatelessWidget { final String title; + final String mainTopic; final String subtitle; final String value; final String dataset; final Color bordercolor; + final String colorPattern; final Color? textcolor; final VoidCallback onTap; final Color backgroundColor; @@ -544,9 +540,11 @@ class InfoCard extends StatelessWidget { required this.value, required this.dataset, required this.bordercolor, + required this.colorPattern, this.textcolor, required this.onTap, required this.backgroundColor, + required this.mainTopic, }) : super(key: key); @override @@ -554,11 +552,16 @@ class InfoCard extends StatelessWidget { double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; + print("colorPatternInfo -$colorPattern $mainTopic"); + final encodedMainTopic = Uri.encodeComponent(mainTopic); + final encodedTitle = Uri.encodeComponent(title); + final encodedKey = Uri.encodeQueryComponent('home'); + + return GestureDetector( onTap: () { - // Call handleInfoCardClick and pass the title and color - handleInfoCardClick( - context, dataset, bordercolor); // Pass 'title' to the function + context.go( + '/chartScreen/$dataset?bgColor=$colorPattern&mainTopic=$encodedMainTopic&title=$encodedTitle&key=$encodedKey'); }, child: Container( height: myheight / 12, diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index 2ea5e160..6f5472c0 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:pocketbase/pocketbase.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -57,6 +58,11 @@ class _BaseScaffoldState extends ConsumerState { _checkUserId(); } + Future _getAppVersion() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + return packageInfo.version; // Returns the app version + } + Future getUserId() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('userId'); // Retrieve the userId @@ -183,98 +189,130 @@ class _BaseScaffoldState extends ConsumerState { ), ), drawer: Drawer( - child: ListView( - children: [ - DrawerHeader( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - children: [ - SizedBox( - width: mywidth / 8, - child: Image( - image: AssetImage('assets/logos/fcsc.png'), - ), - ), - ], - ), - Divider(), - InkWell( - onTap: () => context.go('/editProfile'), - child: Row( + child: Column(children: [ + Expanded( + child: ListView( + children: [ + DrawerHeader( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( children: [ SizedBox( width: mywidth / 8, - height: mywidth / 8, - child: ClipOval( - child: _avatarUrl.isNotEmpty - ? Image( - image: NetworkImage(_avatarUrl), - fit: BoxFit.cover, - width: double.infinity, - height: double.infinity, - ) - : Image( - image: AssetImage( - 'assets/edit_profile/profile.png', - ), - fit: BoxFit.cover, - width: double.infinity, - height: double.infinity, - ), + child: Image( + image: AssetImage('assets/logos/fcsc.png'), ), - //child: Image(image: AssetImage('assets/edit_profile/profile.png')) - ), - SizedBox( - width: mywidth / 20, - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Text(userId ?? 'Loading user...'), // Display userId here - // Text('Mohammad@fcsc.com') - Text(userName ?? 'Loading...'), - Text( - userEmail ?? 'Loading...', - style: TextStyle(fontSize: 12), - ), - ], ), ], ), - ), - ], + Divider(), + InkWell( + onTap: () { if(userId != 'guest')context.go('/editProfile');}, + child: Row( + children: [ + SizedBox( + width: mywidth / 8, + height: mywidth / 8, + child: ClipOval( + child: _avatarUrl.isNotEmpty + ? Image( + image: NetworkImage(_avatarUrl), + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ) + : Image( + image: AssetImage( + 'assets/edit_profile/profile.png', + ), + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ), + ), + //child: Image(image: AssetImage('assets/edit_profile/profile.png')) + ), + SizedBox( + width: mywidth / 20, + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Text(userId ?? 'Loading user...'), // Display userId here + // Text('Mohammad@fcsc.com') + if(userId == "guest")...[ Text(userName ?? 'Guest User'),] + else...[Text(userName ?? 'Loading...'), + Text( + userEmail ?? 'Loading...', + style: TextStyle(fontSize: 12), + ),] + ], + ), + ], + ), + ), + ], + ), ), - ), - ListTile( - leading: Icon(Icons.feedback), - // title: const Text('Feedback'), - title: Text(AppLocalizations.of(context)!.feedback_title), - onTap: () => context.go('/feedback'), - ), - if (role == 'admin') + if(userId != 'guest') ListTile( - leading: Icon(Icons.manage_accounts), - title: Text('Manage User'), - // title: Text( - // AppLocalizations.of(context)!.manage_user, - // ), - onTap: () => context.go('/manageuser'), + leading: Icon(Icons.feedback), + // title: const Text('Feedback'), + title: Text(AppLocalizations.of(context)!.feedback_title), + onTap: () => context.go('/feedback'), ), - ListTile( - leading: Icon(Icons.book), - title: const Text('User Guide'), - onTap: () => context.go('/user-guide'), - ), - ListTile( - leading: Icon(Icons.logout), - title: const Text('Logout'), - onTap: () => logout(), - ), - ], + if (role == 'admin') + ListTile( + leading: Icon(Icons.manage_accounts), + // title: Text('Manage User'), + title: Text( + AppLocalizations.of(context)!.manage_user, + ), + onTap: () => context.go('/manageuser'), + ), + ListTile( + leading: Icon(Icons.book), + title: const Text('User Guide'), + onTap: () => context.go('/user-guide'), + ), + ListTile( + leading: Icon(Icons.logout), + title: const Text('Logout'), + onTap: () => logout(), + ), + ], + ), ), - ), + Padding( + padding: const EdgeInsets.only(bottom: 16.0), + child: FutureBuilder( + future: _getAppVersion(), // Function to get the app version + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return Text( + 'Loading version...', + style: TextStyle(fontSize: 12, color: Colors.grey), + textAlign: TextAlign.center, + ); + } else if (snapshot.hasError) { + return Text( + 'Error fetching version', + style: TextStyle(fontSize: 12, color: Colors.red), + textAlign: TextAlign.center, + ); + } else { + return Text( + 'Version ${snapshot.data}', + style: TextStyle(fontSize: 12, color: Colors.grey), + textAlign: TextAlign.center, + ); + } + }, + ), + ), + ])), bottomNavigationBar: BottomNavigationBar( currentIndex: _getSelectedIndex(currentRoute), onTap: (index) => _onItemTapped(context, index), diff --git a/lib/presentation/routes/drawer_routes/user_guide_route.dart b/lib/presentation/routes/drawer_routes/user_guide_route.dart index 341037ec..3c250988 100644 --- a/lib/presentation/routes/drawer_routes/user_guide_route.dart +++ b/lib/presentation/routes/drawer_routes/user_guide_route.dart @@ -1,99 +1,100 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_carousel_widget/flutter_carousel_widget.dart'; -import 'package:flutter_hooks/flutter_hooks.dart'; -import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:video_player/video_player.dart'; - -import 'package:uae_stat/domain/use_cases/language.dart'; -import 'package:uae_stat/infrastructure/services/img_asset_paths/user_guide_asset_path.dart'; -import 'package:uae_stat/presentation/components/space.dart'; -import 'package:uae_stat/presentation/components/themed_app_bar.dart'; - -typedef _UserGuideType = ({String videoPath, String enDesc, String arDesc}); - -class UserGuideRoute extends HookConsumerWidget { - const UserGuideRoute({super.key}); - - static const List<_UserGuideType> _data = [ - ( - videoPath: UserGuideAssetPath.toggleLanguage, - enDesc: - 'You can toggle between Arabic and English by click the toggle button.', - arDesc: - 'يمكنك التبديل بين العربية والإنجليزية عن طريق النقر على زر التبديل.', - ), - ( - videoPath: UserGuideAssetPath.searchIndicators, - enDesc: - 'You can browse indicators by category or use the search functionality', - arDesc: 'يمكنك تصفح المؤشرات حسب الفئة أو استخدام وظيفة البحث', - ), - ( - videoPath: UserGuideAssetPath.bookmarkIndicators, - enDesc: - 'You can bookmark indicators. You will be notified each time a bookmarked indicator is updated by email or mobile notifications.', - arDesc: - 'يمكنك وضع إشارة مرجعية على المؤشرات. سيتم إعلامك في كل مرة يتم فيها تحديث المؤشر المرجعي عن طريق البريد الإلكتروني أو إشعارات الهاتف المحمول.', - ), - ]; - - @override - Widget build(BuildContext context, WidgetRef ref) { - final e = useState<_UserGuideType>(_data.first); - final carouselOptions = CarouselOptions( - autoPlay: true, - clipBehavior: Clip.none, - onPageChanged: (index, reason) => e.value = _data[index], - aspectRatio: 9 / 17, - disableCenter: true, - enlargeCenterPage: true, - floatingIndicator: true, - autoPlayInterval: const Duration(seconds: 10), - ); - final bodyContent = Column( - children: [ - Expanded( - flex: 3, - child: FlutterCarousel( - options: carouselOptions, - items: _data - .map( - (e) => VideoPlayer( - VideoPlayerController.asset( - e.videoPath, - )..initialize(), - ), - ) - .toList(), - ), - ), - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.fromLTRB(36, 24, 36, 36), - child: Text( - context.translate(e.value.enDesc, e.value.arDesc), - ), - ), - ), - 98.verticalSpace, - ], - ); - final body = Column( - children: [ - ThemedAppBar( - titleText: context.translate( - 'User Guide', - 'دليل المستخدم', - ), - ), - Expanded( - child: bodyContent, - ), - ], - ); - return ColoredBox( - color: Colors.white, - child: SafeArea(child: body), - ); - } -} +// import 'package:flutter/material.dart'; +// import 'package:flutter_carousel_widget/flutter_carousel_widget.dart'; +// import 'package:flutter_hooks/flutter_hooks.dart'; +// import 'package:hooks_riverpod/hooks_riverpod.dart'; +// import 'package:video_player/video_player.dart'; +// +// import 'package:uae_stat/domain/use_cases/language.dart'; +// import 'package:uae_stat/infrastructure/services/img_asset_paths/user_guide_asset_path.dart'; +// import 'package:uae_stat/presentation/components/space.dart'; +// import 'package:uae_stat/presentation/components/themed_app_bar.dart'; +// +// typedef _UserGuideType = ({String videoPath, String enDesc, String arDesc}); +// +// class UserGuideRoute extends HookConsumerWidget { +// const UserGuideRoute({super.key}); +// +// static const List<_UserGuideType> _data = [ +// ( +// videoPath: UserGuideAssetPath.toggleLanguage, +// enDesc: +// 'You can toggle between Arabic and English by click the toggle button.', +// arDesc: +// 'يمكنك التبديل بين العربية والإنجليزية عن طريق النقر على زر التبديل.', +// ), +// ( +// videoPath: UserGuideAssetPath.searchIndicators, +// enDesc: +// 'You can browse indicators by category or use the search functionality', +// arDesc: 'يمكنك تصفح المؤشرات حسب الفئة أو استخدام وظيفة البحث', +// ), +// ( +// videoPath: UserGuideAssetPath.bookmarkIndicators, +// enDesc: +// 'You can bookmark indicators. You will be notified each time a bookmarked indicator is updated by email or mobile notifications.', +// arDesc: +// 'يمكنك وضع إشارة مرجعية على المؤشرات. سيتم إعلامك في كل مرة يتم فيها تحديث المؤشر المرجعي عن طريق البريد الإلكتروني أو إشعارات الهاتف المحمول.', +// ), +// ]; +// +// @override +// Widget build(BuildContext context, WidgetRef ref) { +// final e = useState<_UserGuideType>(_data.first); +// final carouselOptions +// // final carouselOptions = CarouselOptions( +// // autoPlay: true, +// // clipBehavior: Clip.none, +// // onPageChanged: (index, reason) => e.value = _data[index], +// // aspectRatio: 9 / 17, +// // disableCenter: true, +// // enlargeCenterPage: true, +// // floatingIndicator: true, +// // autoPlayInterval: const Duration(seconds: 10), +// // ); +// final bodyContent = Column( +// children: [ +// Expanded( +// flex: 3, +// child: FlutterCarousel( +// options: carouselOptions, +// items: _data +// .map( +// (e) => VideoPlayer( +// VideoPlayerController.asset( +// e.videoPath, +// )..initialize(), +// ), +// ) +// .toList(), +// ), +// ), +// Expanded( +// child: SingleChildScrollView( +// padding: const EdgeInsets.fromLTRB(36, 24, 36, 36), +// child: Text( +// context.translate(e.value.enDesc, e.value.arDesc), +// ), +// ), +// ), +// 98.verticalSpace, +// ], +// ); +// final body = Column( +// children: [ +// ThemedAppBar( +// titleText: context.translate( +// 'User Guide', +// 'دليل المستخدم', +// ), +// ), +// Expanded( +// child: bodyContent, +// ), +// ], +// ); +// return ColoredBox( +// color: Colors.white, +// child: SafeArea(child: body), +// ); +// } +// }