diff --git a/lib/config/my_router.dart b/lib/config/my_router.dart index 8a8a1fc8..98036f8b 100644 --- a/lib/config/my_router.dart +++ b/lib/config/my_router.dart @@ -327,7 +327,7 @@ final GoRouter router = GoRouter( GoRoute( path: '/', //builder: (context, state) => LoginRoute(), - builder: (context, state) => MyHomePage(), + builder: (context, state) => LoginRoute(), ), GoRoute( path: '/login', @@ -342,29 +342,39 @@ final GoRouter router = GoRouter( path: '/register', builder: (context, state) => RegisterScreen(), ), - GoRoute( - path: '/DemoHome/:dataSets', - builder: (context, state) { - final dataSets = state.pathParameters['dataSets']!; - print('Router $dataSets'); - return ChartPage(dataSets: dataSets); - }, - ), - GoRoute( - path: '/Chart/:dataSets', - builder: (context, state) { - final dataSets = state.pathParameters['dataSets']!; - print('Router $dataSets'); - return ChartScreen(dataSets: dataSets); - }, - ), + // GoRoute( + // path: '/DemoHome/:dataSets', + // builder: (context, state) { + // final dataSets = state.pathParameters['dataSets']!; + // print('Router $dataSets'); + // return ChartPage(dataSets: dataSets); + // }, + // ), + // GoRoute( + // path: '/Chart/:dataSets', + // builder: (context, state) { + // final dataSets = state.pathParameters['dataSets']!; + // print('Router $dataSets'); + // return ChartScreen(dataSets: dataSets); + // }, + // ), GoRoute( path: '/chartScreen/:dataSets', builder: (context, state) { + // Retrieve path parameter final dataSets = state.pathParameters['dataSets']!; - print('Router $dataSets'); - // return ChartScreen(dataSets: dataSets); - return ChartScreen1(dataSets: dataSets); + // Retrieve query parameter + final bgColor = state.uri.queryParameters['bgColor'] ?? + '0xFFFFFFFF'; // Default white + + print('Router dataSets: $dataSets'); + print('Router bgColor: $bgColor'); + + // Pass both values to the screen + return ChartScreen1( + dataSets: dataSets, + bgColor: bgColor, + ); }, ), GoRoute( @@ -417,19 +427,20 @@ final GoRouter router = GoRouter( path: '/user-guide', builder: (context, state) => UserGuide(), routes: [ - GoRoute(path: '/features', - name: 'features', - builder: (context, state) => UsingFeatures(), + GoRoute( + path: '/features', + name: 'features', + builder: (context, state) => UsingFeatures(), ), - - GoRoute(path: '/faq', - name: 'faq', - builder: (context, state) => FAQPage(), + GoRoute( + path: '/faq', + name: 'faq', + builder: (context, state) => FAQPage(), ), - - GoRoute(path: '/gettingStarted', - name: 'gettingStarted', - builder: (context, state) => GettingStarted(), + GoRoute( + path: '/gettingStarted', + name: 'gettingStarted', + builder: (context, state) => GettingStarted(), ), ], ), @@ -455,10 +466,7 @@ final GoRouter router = GoRouter( // path: '/uaenumbers', // builder: (context, state) => UaeNumbers(), // ), - GoRoute( - path: '/uaenumbers', - builder: (context,state) => UaeNumbers() - ), + GoRoute(path: '/uaenumbers', builder: (context, state) => UaeNumbers()), GoRoute( path: '/competitiveness', 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 51c0e2aa..258caedc 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,8 +1,11 @@ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:go_router/go_router.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'; +import 'package:http/http.dart' as http; class UaeNumbers extends StatelessWidget { const UaeNumbers({super.key}); @@ -20,13 +23,49 @@ class UaeNumbers extends StatelessWidget { } } -class uaenumberWidget extends StatelessWidget { +class uaenumberWidget extends StatefulWidget { const uaenumberWidget({super.key}); @override + _UaenumberWidgetState createState() => _UaenumberWidgetState(); +} + +class _UaenumberWidgetState extends State { + @override + List homePageData = []; + bool isLoading = true; + + @override + void initState() { + fetchData(); + } + + Future fetchData() 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)); + if (response.statusCode == 200) { + setState(() { + homePageData = json.decode(response.body); + // print("HomeDAta $homePageData"); + isLoading = false; + }); + } else { + throw Exception('Failed to load data'); + } + } catch (e) { + setState(() { + isLoading = false; + }); + print('Error fetching data: $e'); + } + } + Widget build(BuildContext context) { double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; + return Padding( padding: const EdgeInsets.all(16.0), child: ListView( @@ -49,153 +88,112 @@ class uaenumberWidget extends StatelessWidget { SizedBox( height: myheight / 35, ), - CustomExpandableTile( - title: 'ECONOMY', - titleBackgroundColor: economyColor, - children: [ - buildCategoryTitle('National Accounts', economyColor), - buildCardRow([ - buildCard(context, 'GDP (Constant)', '1.62T', '2022 (AED)', - economyColor, economyColor, mywidth / 2.5), - buildCard(context, 'FDI', '1.45T', '2021 (AED)', economyColor, - economyColor, mywidth / 2.5), - ]), - buildCategoryTitle('International Trade', economyColor), - buildCardRow([ - buildCard( - context, - 'Total Trade', - '669.9B', - 'Jan - Mar 2024 (AED)', - economyColor, - economyColor, - mywidth / 2.5), - buildCard( - context, - 'Total Import', - '686B', - 'Jan - Mar 2024 (AED)', - economyColor, - economyColor, - mywidth / 2.5), - ]), - buildCardRow([ - buildCard( - context, - 'Total Export', - '669.9B', - 'Jan - Mar 2024 (AED)', - economyColor, - economyColor, - mywidth / 2.5), - buildCard( - context, - 'Total ReExport', - '686B', - 'Jan - Mar 2024 (AED)', - economyColor, - economyColor, - mywidth / 2.5), - ]), - buildCategoryTitle('Prices', economyColor), - buildCardRow([ - buildCard( - context, - 'Total Export', - '669.9B', - 'Jan - Mar 2024 (AED)', - economyColor, - economyColor, - mywidth / 1.2), - ]), - ], - ), - CustomExpandableTile( - title: 'SOCIAL', - titleBackgroundColor: socialColor, - children: [ - buildCategoryTitle('Population', socialColor), - buildCardRow([ - InkWell( - onTap: () {}, - child: buildCard(context, 'Population', '9.89M', '2024', - socialColor, socialColor, mywidth / 2.5)), - buildCard(context, 'Population Growth', '2.1%', '2023', - socialColor, socialColor, mywidth / 2.5), - ]), - buildCategoryTitle('Vital Statistics', socialColor), - buildCardRow([ - InkWell(onTap : (){ - context.go('/marriages'); - },child: buildCard(context,'Marriages', '96%', '2023',socialColor,socialColor,mywidth/2.5)), - InkWell(onTap : (){},child: buildCard(context,'Divorces', '1.2K', '2024',socialColor,socialColor,mywidth/2.5)), - ]), - buildCategoryTitle('Education', socialColor), - buildCardRow([ - buildCard(context, 'General Education', '96%', '2023', - socialColor, socialColor, mywidth / 2.5), - buildCard(context, 'Higher Education', '1.2K', '2024', - socialColor, socialColor, mywidth / 2.5), - ]), - buildCategoryTitle('Health', socialColor), - buildCardRow([ - buildCard(context, 'Hospital', '96%', '2023', socialColor, - socialColor, mywidth / 2.5), - buildCard(context, 'Clinic and Centres', '1.2K', '2024', - socialColor, socialColor, mywidth / 2.5), - ]), - ], - ), - CustomExpandableTile( - title: 'ENVIRONMENT', - titleBackgroundColor: environmentColor, - children: [ - buildCategoryTitle('Agriculture', environmentColor), - buildCardRow([ - buildCard(context, 'Crops - Total Area', '27°C', 'Average 2024', - environmentColor, environmentColor, mywidth / 2.5), - buildCard(context, 'Livestock', '120mm', '2024', - environmentColor, environmentColor, mywidth / 2.5), - ]), - buildCategoryTitle('Environment', environmentColor), - buildCardRow([ - buildCard(context, 'Climate - Max Temp', '15%', '2024', - environmentColor, environmentColor, mywidth / 2.5), - buildCard(context, 'Climate - Min Temp', '30%', '2023', - environmentColor, environmentColor, mywidth / 2.5), - ]), - buildCardRow([ - buildCard(context, 'Desalinated Produced Water', '15%', '2024', - environmentColor, environmentColor, mywidth / 2.5), - buildCard(context, 'Area of Natural Reserves', '30%', '2023', - environmentColor, environmentColor, mywidth / 2.5), - ]), - buildCategoryTitle('Energy', environmentColor), - buildCardRow([ - buildCard(context, 'Electricity Production', '15%', '2024', - environmentColor, environmentColor, mywidth / 2.5), - buildCard(context, 'Renewable Energy Production', '30%', '2023', - environmentColor, environmentColor, mywidth / 2.5), - ]), - ], - ), + ...homePageData.map((mainTopic) { + String colorPattern = mainTopic['color_pattern']; + print('colorPattern $colorPattern'); + Color backgroundColor = Color(int.parse(colorPattern)); + print('backgroundColor $backgroundColor'); + Color borderColor = backgroundColor; + int index = homePageData.indexOf(mainTopic); + bool isFirstTile = index == 0; + + return CustomExpandableTile( + index: index, + isExpanded: isFirstTile, + title: mainTopic['main_topic'], + titleBackgroundColor: backgroundColor, + children: _buildSubTopics( + mainTopic['sub_topics'] ?? [], + mywidth, + borderColor, + colorPattern, + ), + ); + }), ], ), ); } + List _buildSubTopics(List subTopics, double myWidth, + Color borderColor, String colorPattern) { + print('colorPattern123 $borderColor'); + // Sort sub-topics based on `sub_topic_list_order` + subTopics.sort((a, b) => (a['sub_topic_list_order'] ?? 0) + .compareTo(b['sub_topic_list_order'] ?? 0)); + + return subTopics + .map((subTopic) => Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildCategoryTitle(subTopic['sub_topic'], borderColor), + ..._buildDataSetCards(subTopic['tile_data'] ?? [], myWidth, + borderColor, colorPattern), + ], + )) + .toList(); + } + + List _buildDataSetCards( + List tileData, double myWidth, Color borderColor, colorPattern) { + // Sort datasets based on `data_set_list_order` + tileData.sort((a, b) => (a['data_set_list_order'] ?? 0) + .compareTo(b['data_set_list_order'] ?? 0)); + + List rows = []; + for (int i = 0; i < tileData.length; i += 2) { + // Take 1 or 2 items for the row + final rowItems = tileData.sublist( + i, + i + 2 > tileData.length ? tileData.length : i + 2, + ); + + rows.add( + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: rowItems.map((data) { + // Calculate width: full width for one item, half for two items + final cardWidth = rowItems.length == 1 + ? myWidth + : (myWidth - 16) / 2; // Subtract spacing for padding + + return Expanded( + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 5.0), // Add spacing + child: buildCard( + context, + data['data_set_tile_heading'] ?? '', + data['value'] ?? '', + data['value_source'] ?? '', + data['data_set'] ?? '', + borderColor, + borderColor, + colorPattern, + cardWidth, // Pass the calculated width + ), + ), + ); + }).toList(), + ), + ); + } + return rows; + } + Widget buildCategoryTitle(String title, Color color) { return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - title, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: color, - ), - ), - ); + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Center( + child: Text( + title, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: color, + ), + ), + )); } Widget buildCardRow(List cards) { @@ -212,43 +210,58 @@ class uaenumberWidget extends StatelessWidget { ); } - Widget buildCard(BuildContext context, String title, String value, - String subtitle, Color bordercolor, boldColor, double mywidth) { - // double myheight = MediaQuery.of(context).size.height; - // double mywidth = MediaQuery.of(context).size.width; - return Card( - elevation: 2, - child: Container( - width: mywidth, - decoration: BoxDecoration( - color: Colors.white, // Background color - border: Border.all( - color: bordercolor, // Border color - width: 2, // Border width + Widget buildCard( + BuildContext context, + String title, + String value, + String subtitle, + String data_set, + Color bordercolor, + Color boldColor, + colorPattern, + double mywidth, + ) { + return GestureDetector( + onTap: () { + print(colorPattern); + context.go('/chartScreen/$data_set?bgColor=$colorPattern'); + // context.go('/chartScreen/$data_set'); + }, + child: Card( + elevation: 1, + child: Container( + width: mywidth, + decoration: BoxDecoration( + color: Colors.white, // Background color + border: Border.all( + color: bordercolor, // Border color + width: 2, // Border width + ), + borderRadius: + BorderRadius.circular(12), // Optional: Rounded corners ), - borderRadius: BorderRadius.circular(12), // Optional: Rounded corners - ), - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - title, - textAlign: TextAlign.center, - style: robotoRegular11, - ), - SizedBox(height: 8), - Text(subtitle, textAlign: TextAlign.center, style: subtitleStyle), - SizedBox(height: 8), - Text( - value, - style: TextStyle( - fontSize: 26, - color: boldColor, - fontWeight: FontWeight.bold, + padding: const EdgeInsets.all(3.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + title, + textAlign: TextAlign.center, + style: robotoRegular11, ), - ), - ], + SizedBox(height: 0.2), + Text(subtitle, textAlign: TextAlign.center, style: subtitleStyle), + SizedBox(height: 0.7), + Text( + value, + style: TextStyle( + fontSize: 24, + color: boldColor, + fontWeight: FontWeight.bold, + ), + ), + ], + ), ), ), ); @@ -259,11 +272,15 @@ class CustomExpandableTile extends StatefulWidget { final String title; final Color titleBackgroundColor; final List children; + final int index; + final bool isExpanded; const CustomExpandableTile({ required this.title, required this.titleBackgroundColor, required this.children, + required this.index, + required this.isExpanded, }); @override @@ -271,13 +288,21 @@ class CustomExpandableTile extends StatefulWidget { } class _CustomExpandableTileState extends State { - bool isExpanded = false; + // bool isExpanded = false; + late bool isExpanded; + + @override + void initState() { + super.initState(); + isExpanded = + widget.isExpanded; // Initialize isExpanded based on widget's property + } @override Widget build(BuildContext context) { double myheight = MediaQuery.of(context).size.height; return Card( - elevation: 4, + elevation: 0, child: Column( children: [ GestureDetector( @@ -289,8 +314,9 @@ class _CustomExpandableTileState extends State { child: Container( decoration: BoxDecoration( color: widget.titleBackgroundColor, - borderRadius: BorderRadius.all(Radius.circular(12))), - padding: const EdgeInsets.all(12), + borderRadius: BorderRadius.all(Radius.circular(35))), + padding: const EdgeInsets.only( + left: 16, bottom: 10, top: 10, right: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -307,6 +333,7 @@ class _CustomExpandableTileState extends State { ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, color: Colors.white, + size: 28.0, ), ], ), @@ -317,14 +344,15 @@ class _CustomExpandableTileState extends State { duration: Duration(milliseconds: 300), curve: Curves.easeInOut, width: double.infinity, + height: isExpanded ? myheight * 0.52 : 0, child: isExpanded ? SingleChildScrollView( child: Container( decoration: BoxDecoration( - color: Colors.white, - borderRadius: - BorderRadius.all(Radius.circular(20))), - padding: const EdgeInsets.all(10), + // color: Colors.white, + // borderRadius: BorderRadius.all(Radius.circular(20)) + ), + padding: const EdgeInsets.all(1), child: Column( children: widget.children, ), diff --git a/lib/presentation/Screens/auth_verification/registration.dart b/lib/presentation/Screens/auth_verification/registration.dart index 8b69722d..7c7f207b 100644 --- a/lib/presentation/Screens/auth_verification/registration.dart +++ b/lib/presentation/Screens/auth_verification/registration.dart @@ -189,11 +189,12 @@ class _RegisterScreenState extends State { print('adminToken- ${adminToken}'); // Create user in PocketBase final response = await pb.collection('users').create(body: { - 'username': _usernameController.text, + 'uname': _usernameController.text, 'email': _emailController.text, 'password': _passwordController.text, 'passwordConfirm': _passwordController.text, 'status': 'Pending', + 'role': 'user', }, headers: { 'Authorization': adminToken }); @@ -316,15 +317,7 @@ class _RegisterScreenState extends State { ), SizedBox(height: 10), ElevatedButton( - onPressed: () => { - Navigator.pushReplacement( - context, - MaterialPageRoute(builder: (context) => LoginRoute() - - //ProfileScreen(userId: userID) - ), - ), - }, + onPressed: () => {context.go('/')}, child: Text( 'Go to Login', ), diff --git a/lib/presentation/Screens/charts/chart.dart b/lib/presentation/Screens/charts/chart.dart index a34bf4a4..a4a3084e 100644 --- a/lib/presentation/Screens/charts/chart.dart +++ b/lib/presentation/Screens/charts/chart.dart @@ -1,1211 +1,1083 @@ -import 'dart:collection'; -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:intl/intl.dart'; -import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package -import 'package:http/http.dart' as http; - -void main() { - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - home: ChartScreen(dataSets: ''), - ); - } -} - -class ChartScreen extends StatefulWidget { - final String dataSets; - const ChartScreen({Key? key, required this.dataSets}); - @override - ChartScreenState createState() => ChartScreenState(); -} - -class ChartScreenState extends State { - List> selectedFiltersStorage = []; - List chartsData = []; - List cardData = []; - List filterData = []; - List> originalChartsData = []; - List> originalCardData = []; - bool isLoading = true; - List isChartData = []; - List nonChartData = []; - // dynamic firstYear; - // dynamic lastYearValue; - // dynamic secondLastYearValue; - // dynamic lastYear; - // dynamic secondLastYear; - // dynamic roundedAverage; - final GlobalKey _scaffoldKey = GlobalKey(); - - @override - void initState() { - super.initState(); - fetchChartData(widget.dataSets); - } - - Future fetchChartData(String dataSets) async { - const baseUrl = - 'https://pb.venbait.in/api/getDataSet'; // Replace with your server URL - final url = Uri.parse('$baseUrl?dataset=$dataSets'); - - try { - final response = await http.get(url); - if (response.statusCode == 200) { - final List data = jsonDecode(response.body); - - // Variables to store filtered data - for (var item in data) { - if (item['is_chart'] == 'true') { - isChartData.add(item); - } else if (item['is_chart'] == 'false') { - nonChartData.add(item); - } - } - - // Print or use the filtered data - print('Chart Data: $isChartData'); - print('Non-Chart Data: $nonChartData'); - - // Save original data for reset - originalChartsData = List.from(isChartData); // Store original data - originalCardData = List.from(nonChartData); // Store original data - setState(() { - chartsData = isChartData; - cardData = nonChartData; - print('chartsData $chartsData'); - print('cardData $cardData'); - isLoading = false; - }); - } else { - throw Exception('Failed to load data'); - } - } catch (error) { - setState(() { - isLoading = false; - }); - print('Error fetching data: $error'); - } - } - - // void extractLastTwoYearsData(Map nonChartData) { - // try { - // final responseData = nonChartData['response']; - // final response = responseData is String - // ? jsonDecode(responseData) as List - // : responseData as List; - // - // // Extracting the time periods and sorting them in descending order - // final sortedResponse = response - // .map((item) => item['ObsKey']['TIME_PERIOD'].toString()) - // .toList() - // ..sort((a, b) => b.compareTo(a)); - // - // // Get the last year and second-to-last year - // lastYear = sortedResponse.isNotEmpty ? sortedResponse.first : null; - // secondLastYear = sortedResponse.length > 1 ? sortedResponse[1] : null; - // firstYear = sortedResponse.isNotEmpty - // ? sortedResponse.last - // : null; // First year (oldest) - // - // // Extract the corresponding values - // lastYearValue = response.firstWhere( - // (item) => item['ObsKey']['TIME_PERIOD'].toString() == lastYear, - // orElse: () => null)?['ObsValue']['Value']; - // - // secondLastYearValue = response.firstWhere( - // (item) => item['ObsKey']['TIME_PERIOD'].toString() == secondLastYear, - // orElse: () => null)?['ObsValue']['Value']; - // - // // Print or use the extracted values - // print('Last Year: $lastYear, Value: $lastYearValue'); - // print( - // 'Second-to-Last Year: $secondLastYear, Value: $secondLastYearValue'); - // - // // Extract all values and calculate the sum and average - // List allValues = response - // .map((item) => - // double.tryParse(item['ObsValue']['Value'].toString()) ?? 0.0) - // .toList(); - // - // double sum = allValues.reduce((a, b) => a + b); - // double average = sum / allValues.length; - // // Round the average to the nearest whole number - // roundedAverage = average.round(); - // - // // Print sum and rounded average - // print('Sum of Values: $sum'); - // print('Rounded Average Value: $roundedAverage'); - // } catch (e) { - // print('Error processing data: $e'); - // } - // } - -// When decoding JSON, you can convert the LinkedMap to a Map - Map processNonChartData(item) { - print('processNonChartData check test case'); - try { - final response = item['response']; - - // If 'response' is a LinkedMap, convert it to Map - final List responseData = response is LinkedHashMap - ? Map.from( - response) // Convert to Map - : response; - - // Now you can process the Map as expected - final sortedResponse = List.from( - responseData.map((e) => e['ObsKey']['TIME_PERIOD'].toString())) - ..sort((a, b) => b.compareTo(a)); - - String? lastYear = - sortedResponse.isNotEmpty ? sortedResponse.first : null; - String? secondLastYear = - sortedResponse.length > 1 ? sortedResponse[1] : null; - String? firstYear = - sortedResponse.isNotEmpty ? sortedResponse.last : null; - - String? lastYearValue = response.firstWhere( - (item) => item['ObsKey']['TIME_PERIOD'].toString() == lastYear, - orElse: () => null, - )?['ObsValue']['Value']; - - String? secondLastYearValue = response.firstWhere( - (item) => item['ObsKey']['TIME_PERIOD'].toString() == secondLastYear, - orElse: () => null, - )?['ObsValue']['Value']; - - List allValues = responseData - .map((item) => - double.tryParse(item['ObsValue']['Value'].toString()) ?? 0.0) - .toList(); - - double sum = allValues.reduce((a, b) => a + b); - double average = sum / allValues.length; - double roundedAverage = - average.roundToDouble(); // Rounds to the nearest integer - - return { - 'lastYear': lastYear ?? 'NA', - 'secondLastYear': secondLastYear ?? 'NA', - 'firstYear': firstYear ?? 'NA', - 'lastYearValue': lastYearValue ?? 'NA', - 'secondLastYearValue': secondLastYearValue ?? 'NA', - 'roundedAverage': roundedAverage, - 'allValues': allValues.isEmpty ? [0.0] : allValues, - }; - } catch (e) { - print('Error processing data: $e'); - return {}; - } - } - - String capitalizeAndSplit(String text) { - if (text.isEmpty) return text; - return text - .split('_') // Split by underscores - .map((word) => word[0].toUpperCase() + word.substring(1).toLowerCase()) - .join(' '); // Join words with a space - } - - List parseLineChartData(List response) { - return response.map((entry) { - // Attempt to fetch the TIMEPERIOD - final rawTimePeriod = - entry['ObsKey']?['Year'] ?? entry['ObsKey']?['TIME_PERIOD']; - final formattedTimePeriod = rawTimePeriod != null - ? rawTimePeriod.toString() // Use as-is if valid - : 'Unknown'; // Fallback value if null - - // Parse the value or fallback to 0.0 - final value = - double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? - 0.0; - - return ChartData( - timePeriod: formattedTimePeriod, - value: value, - ); - }).toList(); - } - - List parseBarChartData(List response) { - return response - .asMap() - .entries - .map((entry) => ChartData( - timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ?? - 'Unknown', // Fallback to 'Unknown' if null - value: double.tryParse( - entry.value['ObsValue']['Value'].toString()) ?? - 0.0, // Handle null/invalid value - )) - .toList(); - } - -// Update parseColumnChartData for column chart - List parseColumnChartData(List response) { - return response.map((entry) { - // Extract TIME_PERIOD as x - final x = - int.tryParse(entry['ObsKey']?['TIME_PERIOD']?.toString() ?? '0') ?? 0; - // Extract Value as y - final y = - double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? - 0.0; - return ChartData(xInt: x, y: y); - }).toList(); - } - -// Update parseScatterChartData for scatter chart - List parseScatterChartData(List response) { - return response.map((entry) { - // Parse TIME_PERIOD into DateTime - final timePeriod = entry['ObsKey']?['TIME_PERIOD']; - final x = timePeriod != null - ? DateTime.tryParse('$timePeriod-01-01') // Convert year to DateTime - : DateTime.now(); // Fallback to current date if null - - // Parse Value into double - final y = - double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? - 0.0; - - return ChartData(xDateTime: x, y: y); - }).toList(); - } - - List parseStackedBarChartData( - dynamic chartData, String groupByKey, String groupByValue) { - return chartData['response'] - .where((entry) => entry['ObsKey'][groupByKey] == groupByValue) - .map((entry) { - // Specify the type here - return ChartData( - x: entry['ObsKey']['TIME_PERIOD'], - y: double.tryParse(entry['ObsValue']['Value']) ?? 0.0, - ); - }).toList(); - } - - 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 - if (selectedFilters.every((filter) => filter['filter_data'].isEmpty)) { - setState(() { - chartsData = List.from(originalChartsData); // Restore the original data - cardData = List.from(originalCardData); // Restore the original data - }); - print('Returning as no filters are selected'); - Navigator.pop(context); - return; // Exit early since all filter_data are empty - } - - // Loop through each chart data in the `data` list - List filteredData = []; - for (var chart in data) { - // Extract response data for filtering - List response = chart['response'] ?? []; - - // Filter the response based on selected filters - var chartFilteredData = 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']; - - // 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 (chartFilteredData.isNotEmpty) { - filteredData.add({ - ...chart, // Include all other properties of the chart object - 'response': chartFilteredData, // Only include filtered response data - }); - } - } - - 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']; - - // 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 - }); - } - } - - // Update the chartsData with the filtered data - setState(() { - chartsData = filteredData; - cardData = filteredCardData; - }); - - print("Filtered Data: $filteredData"); - print("Filtered Card: $filteredCardData"); - - // Go back after applying filters - Navigator.pop(context); - } - - Widget buildChart(dynamic chartData) { - // Extract group_by dynamically from the chartData - String groupByKey = chartData['group_by']; - - // Extract unique values for the group_by key dynamically - Set groupByValues = {}; - chartData['response'].forEach((entry) { - groupByValues.add(entry['ObsKey'][groupByKey]); - }); - switch (chartData['chart_type']) { - case 'scatter': - return SfCartesianChart( - primaryXAxis: DateTimeAxis(), // Ensure it's DateTimeAxis - tooltipBehavior: TooltipBehavior(enable: true), - title: ChartTitle(text: 'Divorces'), - series: [ - ScatterSeries( - dataSource: parseScatterChartData(chartData['response']), - xValueMapper: (ChartData data, _) => - data.xDateTime ?? DateTime.now(), - yValueMapper: (ChartData data, _) => data.y, - name: chartData['dataset'], - dataLabelSettings: DataLabelSettings(isVisible: true), - ), - ], - ); - case 'column': - return SfCartesianChart( - primaryXAxis: NumericAxis( - title: AxisTitle(text: 'Year'), - interval: 1, // Ensure no fractional intervals - ), - title: ChartTitle(text: 'Marriages'), - series: >[ - ColumnSeries( - dataSource: parseColumnChartData(chartData['response']), - xValueMapper: (ChartData data, _) => data.xInt ?? 0, - yValueMapper: (ChartData data, _) => data.y, - name: chartData['dataset'], - color: Color(0xFF7DAFBC), - width: 0.8, - spacing: 0.2, - dataLabelSettings: - DataLabelSettings(isVisible: true), // Show data labels - ), - ], - ); - case 'line': - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: 'Line Chart'), - legend: Legend(isVisible: true), - tooltipBehavior: TooltipBehavior(enable: true), - series: >[ - LineSeries( - dataSource: parseLineChartData(chartData['response']), - xValueMapper: (ChartData data, _) => data.timePeriod ?? '', - yValueMapper: (ChartData data, _) => data.value ?? 0.0, - name: 'Sales', - dataLabelSettings: DataLabelSettings(isVisible: true), - ), - ], - ); - case 'bar': - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: 'Bar Chart'), - tooltipBehavior: TooltipBehavior(enable: true), - series: >[ - BarSeries( - dataSource: parseBarChartData(chartData['response']), - xValueMapper: (ChartData data, _) => data.timePeriod ?? '', - yValueMapper: (ChartData data, _) => data.value ?? 0.0, - name: 'Gold', - color: Color.fromRGBO(8, 142, 255, 1), - ), - ], - ); - case 'stacked_bar': - String chartTitle = - capitalizeAndSplit(chartData['chart_heading'] ?? ''); - // Define a list of colors - final List uniqueColors = [ - Color(0xFF648CBA), - Color(0xFF90B0D5), - Color(0xFF98BCE5), - Color(0xFFA7B5C5), - Color(0xFFBED3EC), - Color(0xFFD4E3F4), - ]; - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: chartData['chart_heading']), - legend: Legend( - isVisible: true, - position: LegendPosition.bottom, - overflowMode: LegendItemOverflowMode.scroll, - ), - tooltipBehavior: TooltipBehavior( - enable: true, // Enable tooltips - format: 'point.x : point.y', // Custom tooltip format - ), - series: >[ - for (int i = 0; i < groupByValues.length; i++) - StackedBarSeries( - dataSource: parseStackedBarChartData( - chartData, groupByKey, groupByValues.elementAt(i)), - xValueMapper: (ChartData data, _) => data.x ?? '', - yValueMapper: (ChartData data, _) => data.y, - name: groupByValues.elementAt(i), - color: uniqueColors[i % uniqueColors.length], - enableTooltip: true, - ), - ], - ); - case 'stacked_column': - String chartTitle = - capitalizeAndSplit(chartData['chart_heading'] ?? ''); - // Define a list of colors - final List uniqueColors = [ - Color(0xFF648CBA), - Color(0xFF90B0D5), - Color(0xFF98BCE5), - Color(0xFFA7B5C5), - Color(0xFFBED3EC), - Color(0xFFD4E3F4), - ]; - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: chartData['chart_heading']), - legend: Legend( - isVisible: true, - position: LegendPosition.bottom, - overflowMode: LegendItemOverflowMode.scroll, - ), - tooltipBehavior: TooltipBehavior( - enable: true, // Enable tooltips - format: 'point.x : point.y', // Custom tooltip format - ), - series: >[ - for (int i = 0; i < groupByValues.length; i++) - StackedColumnSeries( - dataSource: parseStackedBarChartData( - chartData, groupByKey, groupByValues.elementAt(i)), - xValueMapper: (ChartData data, _) => data.x ?? '', - yValueMapper: (ChartData data, _) => data.y, - name: groupByValues.elementAt(i), - color: uniqueColors[i % uniqueColors.length], - enableTooltip: true, - ), - ], - ); - default: - return Center(child: Text('Unknown chart type')); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - key: _scaffoldKey, - backgroundColor: Colors.brown[200]!, - appBar: AppBar( - backgroundColor: Colors.brown[200]!, - elevation: 0, - leading: IconButton( - icon: Icon(Icons.arrow_back_ios_new, color: Colors.white), - onPressed: () { - context.go('/myhomepage'); - }, - ), - title: Text( - 'UAE Numbers', - style: TextStyle(color: Colors.white), - ), - ), - body: isLoading - ? Center(child: CircularProgressIndicator()) - : Column( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment - .end, // Aligns the row's content to the right - children: [ - Row( - children: [ - Text( - 'Bookmark', - style: const TextStyle( - fontSize: 16, - color: Colors.white, - ), - ), - Icon(Icons.bookmark_add_outlined, - color: Colors.white, size: 18), - ], - ), - SizedBox(width: 10), // Horizontal space between items - Row( - children: [ - Text( - 'Share', - style: const TextStyle( - fontSize: 16, - color: Colors.white, - ), - ), - Icon(Icons.share_sharp, - color: Colors.white, size: 18), - ], - ), - SizedBox(width: 10), // Horizontal space between items - IconButton( - icon: Icon(Icons.filter_alt_outlined, - color: Colors.white, size: 18), - onPressed: () { - showRightSideModal( - context, chartsData[0]["filters"], chartsData); - }, - ), - ], - ), - ], - ), - - // Padding( - // padding: const EdgeInsets.all(16.0), - // child: Align( - // alignment: Alignment.topRight, - // child: IconButton( - // icon: Icon(Icons.filter_list, color: Colors.white), - // onPressed: () { - // showRightSideModal( - // context, chartsData[0]["filters"], chartsData); - // }, - // ), - // ), - // ), - Expanded( - child: ListView( - children: [ - // nonChartData Cards - GridView.builder( - itemCount: cardData.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, // 2 cards per row - crossAxisSpacing: 10, - mainAxisSpacing: 10, - childAspectRatio: 177.5 / - 180, // Adjusted to maintain width and fixed height - ), - 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 data = processNonChartData(item); - print('processNonChartData'); - - if (chart_type == 'total') { - return Card( - margin: const EdgeInsets.all(10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 4, - child: Padding( - padding: const EdgeInsets.all(12.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.public, - color: Color(0xFF90B0D5), size: 22), - const SizedBox(height: 5), - Text( - '${chart_heading ?? 'NA'}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - Text( - '(${data['lastYear'] ?? 'NA'})', - style: const TextStyle( - fontSize: 10, color: Colors.grey), - ), - Text( - '${data['lastYearValue'] ?? 'NA'}', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Color(0xFF90B0D5), - ), - ), - const Divider( - color: Colors.grey, thickness: 1), - Text( - '${data['secondLastYearValue'] ?? 'NA'}', - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Color(0xFF90B0D5), - ), - ), - Text( - '(${data['secondLastYear'] ?? 'NA'})', - style: const TextStyle( - fontSize: 10, color: Colors.grey), - ), - ], - ), - ), - ); - } else if (chart_type == 'average') { - return Card( - margin: const EdgeInsets.all(10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), - ), - elevation: 4, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.analytics, - color: Color(0xFF90B0D5), size: 22), - const SizedBox(height: 5), - Text( - '${chart_heading ?? 'NA'}', - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Colors.black87, - ), - ), - Text( - '(${data['firstYear'] ?? 'NA'} - ${data['lastYear'] ?? 'NA'})', - style: const TextStyle( - fontSize: 10, color: Colors.grey), - ), - Text( - '${data['roundedAverage'] ?? 'NA'}', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Color(0xFF90B0D5), - ), - ), - ], - ), - ), - ); - } else { - return const SizedBox - .shrink(); // Ignore unknown KPIs - } - }, - ), - // chartsData Cards - ListView.builder( - itemCount: chartsData.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, index) { - return Card( - margin: const EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 10), - Container( - height: 300, - child: buildChart(chartsData[index]), - ), - ], - ), - ), - ); - }, - ), - ], - ), - ), - ], - ), - ); - } - - void showRightSideModal(BuildContext context, List filters, List data) { - // Initialize selected filters structure from the stored value, if exists - List> selectedFilters = - selectedFiltersStorage.isNotEmpty - ? List.from(selectedFiltersStorage) // Use stored filters - : filters.map((filter) { - return {"filter_key": filter["filter_key"], "filter_data": []}; - }).toList(); // Or initialize empty filters - - showModalBottomSheet( - context: context, - isScrollControlled: true, - backgroundColor: Colors.transparent, - builder: (BuildContext context) { - return Align( - alignment: Alignment.centerRight, - child: Container( - width: MediaQuery.of(context).size.width * 0.7, - height: MediaQuery.of(context).size.height, - color: Colors.white, - child: Column( - children: [ - // Header Section - Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Filters', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - IconButton( - icon: Icon(Icons.close), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ), - // Filters Section - Expanded( - child: ListView.builder( - itemCount: filters.length, - itemBuilder: (context, index) { - final filter = filters[index]; - final filterKey = filter["filter_key"]; - final filterData = filter["filter_data"]; - - return StatefulBuilder( - builder: (context, setState) { - // Get the corresponding selected filter object - var selectedFilter = selectedFilters - .firstWhere((f) => f["filter_key"] == filterKey); - - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 16.0, vertical: 8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - filterKey, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - InkWell( - onTap: () { - showDialog( - context: context, - builder: (BuildContext context) { - return StatefulBuilder( - builder: (context, dialogSetState) { - return AlertDialog( - title: Text('Select $filterKey'), - content: SingleChildScrollView( - child: ListBody( - children: filterData - .map((value) { - return CheckboxListTile( - title: Text( - value.toString()), - value: selectedFilter[ - "filter_data"] - .contains(value), - onChanged: - (bool? isChecked) { - dialogSetState(() { - if (isChecked == - true) { - selectedFilter[ - "filter_data"] - .add(value); - } else { - selectedFilter[ - "filter_data"] - .remove(value); - } - }); - }, - ); - }).toList(), - ), - ), - actions: [ - TextButton( - onPressed: () { - // Store the updated selected filters after dialog closes - setState(() {}); - Navigator.pop(context); - }, - child: Text('OK'), - ), - ], - ); - }, - ); - }, - ); - }, - child: Container( - width: double.infinity, - padding: EdgeInsets.symmetric( - horizontal: 16.0, vertical: 12.0), - decoration: BoxDecoration( - border: Border.all(color: Colors.grey), - borderRadius: BorderRadius.circular(8.0), - ), - child: Wrap( - spacing: 8.0, - runSpacing: 4.0, - children: - selectedFilter["filter_data"].isEmpty - ? [ - Text('Select $filterKey', - style: TextStyle( - color: Colors.grey)) - ] - : selectedFilter["filter_data"] - .map((value) { - return Chip( - label: Text(value), - onDeleted: () { - setState(() { - selectedFilter[ - "filter_data"] - .remove(value); - }); - }, - ); - }).toList(), - ), - ), - ), - ], - ), - ); - }, - ); - }, - ), - ), - // Button Section - Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - ElevatedButton( - onPressed: () { - // Clear all selected filters - selectedFilters.forEach((filter) { - filter["filter_data"].clear(); - }); - setState(() { - chartsData = List.from(originalChartsData); - cardData = List.from(originalCardData); - }); - // Reset the selectedFiltersStorage to empty when clearing - selectedFiltersStorage.clear(); - Navigator.pop(context); - }, - child: Text('Clear'), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.grey, - ), - ), - ElevatedButton( - onPressed: () { - print( - "Selected Filters before applying: $selectedFilters"); - setState(() { - chartsData = originalChartsData; - cardData = originalCardData; - }); - applyFilters(context, filters, chartsData, cardData, - selectedFilters); - // Save the selected filters to storage after applying - selectedFiltersStorage = List.from(selectedFilters); - }, - child: Text('Apply Filter'), - style: ElevatedButton.styleFrom( - backgroundColor: Colors.blue, - ), - ), - ], - ), - ), - ], - ), - ), - ); - }, - ); - } -} - -class ChartData { - final int? xInt; // For column and other charts, using int for x - final DateTime? xDateTime; // For scatter charts, using DateTime for x - final double? y; // For column and other charts - final String? timePeriod; // For line and bar charts - final double? value; // For line and bar charts - late final double? y2; // For secondary data (stacked column chart) - late final double? y3; // For tertiary data (stacked column chart) - final double? y4; // For quaternary data (stacked column chart) - final String? x; // For stacked bar charts, using String for x - - // Constructor that handles all chart cases - ChartData({ - this.xInt, - this.xDateTime, - this.y, - this.timePeriod, - this.value, - this.y2, - this.y3, - this.y4, - this.x, - }); -} - -// [ -// { -// chart_heading :"Guest Nights by Region" -// chart_type :"fl_stacked_bar" -// dataset :"hotels" -// group_by :"GUEST_REGION" -// is_chart :"true" -// kpi :"hotel_occupancy_rate_chart_1" -// main_id :"economy" -// response:[ -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "AF", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2016", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "2659446" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "AF", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2017", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "2721363" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "ASC", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2016", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "16454601" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "ASC", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2017", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "18960773" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "UAE", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2019", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "14286573" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "UAE", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2020", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "13982919" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "EC", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2019", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "19917654" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "EC", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2020", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "10041664" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "OC", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2017", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "1260359" -// } -// }, -// { -// "ObsKey": { -// "FREQ": "A", -// "GUEST_REGION": "OC", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", -// "REF_AREA": "AE", -// "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2018", -// "UNIT_MEASURE": "NUMBER" -// }, -// "ObsValue": { -// "Value": "1127282" -// } +// import 'dart:collection'; +// import 'dart:convert'; +// import 'package:flutter/material.dart'; +// import 'package:go_router/go_router.dart'; +// import 'package:intl/intl.dart'; +// import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package +// import 'package:http/http.dart' as http; +// +// void main() { +// runApp(MyApp()); // } // -// ] -// sub_id :"tourism" -// url :"https://releaseeuaestat.fcsc.gov.ae/rest/data/FCSA,DF_GUEST_REGION,4.3.0/...A..GUN.OTH+OC+AF+EC+AM+AC+ASC+GCC+UAE.?startPeriod=2016&dimensionAtObservation=AllDimensions" +// class MyApp extends StatelessWidget { +// @override +// Widget build(BuildContext context) { +// return MaterialApp( +// home: ChartScreen(dataSets: ''), +// ); +// } // } -// ] +// +// class ChartScreen extends StatefulWidget { +// final String dataSets; +// const ChartScreen({Key? key, required this.dataSets}); +// @override +// ChartScreenState createState() => ChartScreenState(); +// } +// +// class ChartScreenState extends State { +// List> selectedFiltersStorage = []; +// List chartsData = []; +// List cardData = []; +// List filterData = []; +// List> originalChartsData = []; +// List> originalCardData = []; +// bool isLoading = true; +// List isChartData = []; +// List nonChartData = []; +// // dynamic firstYear; +// // dynamic lastYearValue; +// // dynamic secondLastYearValue; +// // dynamic lastYear; +// // dynamic secondLastYear; +// // dynamic roundedAverage; +// final GlobalKey _scaffoldKey = GlobalKey(); +// +// @override +// void initState() { +// super.initState(); +// fetchChartData(widget.dataSets); +// } +// +// Future fetchChartData(String dataSets) async { +// const baseUrl = +// 'https://pb.venbait.in/api/getDataSet'; // Replace with your server URL +// final url = Uri.parse('$baseUrl?dataset=$dataSets'); +// +// try { +// final response = await http.get(url); +// if (response.statusCode == 200) { +// final List data = jsonDecode(response.body); +// +// // Variables to store filtered data +// for (var item in data) { +// if (item['is_chart'] == 'true') { +// isChartData.add(item); +// } else if (item['is_chart'] == 'false') { +// nonChartData.add(item); +// } +// } +// +// // Print or use the filtered data +// print('Chart Data: $isChartData'); +// print('Non-Chart Data: $nonChartData'); +// +// // Save original data for reset +// originalChartsData = List.from(isChartData); // Store original data +// originalCardData = List.from(nonChartData); // Store original data +// setState(() { +// chartsData = isChartData; +// cardData = nonChartData; +// print('chartsData $chartsData'); +// print('cardData $cardData'); +// isLoading = false; +// }); +// } else { +// throw Exception('Failed to load data'); +// } +// } catch (error) { +// setState(() { +// isLoading = false; +// }); +// print('Error fetching data: $error'); +// } +// } +// +// // void extractLastTwoYearsData(Map nonChartData) { +// // try { +// // final responseData = nonChartData['response']; +// // final response = responseData is String +// // ? jsonDecode(responseData) as List +// // : responseData as List; +// // +// // // Extracting the time periods and sorting them in descending order +// // final sortedResponse = response +// // .map((item) => item['ObsKey']['TIME_PERIOD'].toString()) +// // .toList() +// // ..sort((a, b) => b.compareTo(a)); +// // +// // // Get the last year and second-to-last year +// // lastYear = sortedResponse.isNotEmpty ? sortedResponse.first : null; +// // secondLastYear = sortedResponse.length > 1 ? sortedResponse[1] : null; +// // firstYear = sortedResponse.isNotEmpty +// // ? sortedResponse.last +// // : null; // First year (oldest) +// // +// // // Extract the corresponding values +// // lastYearValue = response.firstWhere( +// // (item) => item['ObsKey']['TIME_PERIOD'].toString() == lastYear, +// // orElse: () => null)?['ObsValue']['Value']; +// // +// // secondLastYearValue = response.firstWhere( +// // (item) => item['ObsKey']['TIME_PERIOD'].toString() == secondLastYear, +// // orElse: () => null)?['ObsValue']['Value']; +// // +// // // Print or use the extracted values +// // print('Last Year: $lastYear, Value: $lastYearValue'); +// // print( +// // 'Second-to-Last Year: $secondLastYear, Value: $secondLastYearValue'); +// // +// // // Extract all values and calculate the sum and average +// // List allValues = response +// // .map((item) => +// // double.tryParse(item['ObsValue']['Value'].toString()) ?? 0.0) +// // .toList(); +// // +// // double sum = allValues.reduce((a, b) => a + b); +// // double average = sum / allValues.length; +// // // Round the average to the nearest whole number +// // roundedAverage = average.round(); +// // +// // // Print sum and rounded average +// // print('Sum of Values: $sum'); +// // print('Rounded Average Value: $roundedAverage'); +// // } catch (e) { +// // print('Error processing data: $e'); +// // } +// // } +// +// // When decoding JSON, you can convert the LinkedMap to a Map +// Map processNonChartData(item) { +// print('processNonChartData check test case'); +// try { +// final response = item['response']; +// +// // If 'response' is a LinkedMap, convert it to Map +// final List responseData = response is LinkedHashMap +// ? Map.from( +// response) // Convert to Map +// : response; +// +// // Now you can process the Map as expected +// final sortedResponse = List.from( +// responseData.map((e) => e['ObsKey']['TIME_PERIOD'].toString())) +// ..sort((a, b) => b.compareTo(a)); +// +// String? lastYear = +// sortedResponse.isNotEmpty ? sortedResponse.first : null; +// String? secondLastYear = +// sortedResponse.length > 1 ? sortedResponse[1] : null; +// String? firstYear = +// sortedResponse.isNotEmpty ? sortedResponse.last : null; +// +// String? lastYearValue = response.firstWhere( +// (item) => item['ObsKey']['TIME_PERIOD'].toString() == lastYear, +// orElse: () => null, +// )?['ObsValue']['Value']; +// +// String? secondLastYearValue = response.firstWhere( +// (item) => item['ObsKey']['TIME_PERIOD'].toString() == secondLastYear, +// orElse: () => null, +// )?['ObsValue']['Value']; +// +// List allValues = responseData +// .map((item) => +// double.tryParse(item['ObsValue']['Value'].toString()) ?? 0.0) +// .toList(); +// +// double sum = allValues.reduce((a, b) => a + b); +// double average = sum / allValues.length; +// double roundedAverage = +// average.roundToDouble(); // Rounds to the nearest integer +// +// return { +// 'lastYear': lastYear ?? 'NA', +// 'secondLastYear': secondLastYear ?? 'NA', +// 'firstYear': firstYear ?? 'NA', +// 'lastYearValue': lastYearValue ?? 'NA', +// 'secondLastYearValue': secondLastYearValue ?? 'NA', +// 'roundedAverage': roundedAverage, +// 'allValues': allValues.isEmpty ? [0.0] : allValues, +// }; +// } catch (e) { +// print('Error processing data: $e'); +// return {}; +// } +// } +// +// String capitalizeAndSplit(String text) { +// if (text.isEmpty) return text; +// return text +// .split('_') // Split by underscores +// .map((word) => word[0].toUpperCase() + word.substring(1).toLowerCase()) +// .join(' '); // Join words with a space +// } +// +// List parseLineChartData(List response) { +// return response.map((entry) { +// // Attempt to fetch the TIMEPERIOD +// final rawTimePeriod = +// entry['ObsKey']?['Year'] ?? entry['ObsKey']?['TIME_PERIOD']; +// final formattedTimePeriod = rawTimePeriod != null +// ? rawTimePeriod.toString() // Use as-is if valid +// : 'Unknown'; // Fallback value if null +// +// // Parse the value or fallback to 0.0 +// final value = +// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? +// 0.0; +// +// return ChartData( +// timePeriod: formattedTimePeriod, +// value: value, +// ); +// }).toList(); +// } +// +// List parseBarChartData(List response) { +// return response +// .asMap() +// .entries +// .map((entry) => ChartData( +// timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ?? +// 'Unknown', // Fallback to 'Unknown' if null +// value: double.tryParse( +// entry.value['ObsValue']['Value'].toString()) ?? +// 0.0, // Handle null/invalid value +// )) +// .toList(); +// } +// +// // Update parseColumnChartData for column chart +// List parseColumnChartData(List response) { +// return response.map((entry) { +// // Extract TIME_PERIOD as x +// final x = +// int.tryParse(entry['ObsKey']?['TIME_PERIOD']?.toString() ?? '0') ?? 0; +// // Extract Value as y +// final y = +// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? +// 0.0; +// return ChartData(xInt: x, y: y); +// }).toList(); +// } +// +// // Update parseScatterChartData for scatter chart +// List parseScatterChartData(List response) { +// return response.map((entry) { +// // Parse TIME_PERIOD into DateTime +// final timePeriod = entry['ObsKey']?['TIME_PERIOD']; +// final x = timePeriod != null +// ? DateTime.tryParse('$timePeriod-01-01') // Convert year to DateTime +// : DateTime.now(); // Fallback to current date if null +// +// // Parse Value into double +// final y = +// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? +// 0.0; +// +// return ChartData(xDateTime: x, y: y); +// }).toList(); +// } +// +// List parseStackedBarChartData( +// dynamic chartData, String groupByKey, String groupByValue) { +// return chartData['response'] +// .where((entry) => entry['ObsKey'][groupByKey] == groupByValue) +// .map((entry) { +// // Specify the type here +// return ChartData( +// x: entry['ObsKey']['TIME_PERIOD'], +// y: double.tryParse(entry['ObsValue']['Value']) ?? 0.0, +// ); +// }).toList(); +// } +// +// 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 +// if (selectedFilters.every((filter) => filter['filter_data'].isEmpty)) { +// setState(() { +// chartsData = List.from(originalChartsData); // Restore the original data +// cardData = List.from(originalCardData); // Restore the original data +// }); +// print('Returning as no filters are selected'); +// Navigator.pop(context); +// return; // Exit early since all filter_data are empty +// } +// +// // Loop through each chart data in the `data` list +// List filteredData = []; +// for (var chart in data) { +// // Extract response data for filtering +// List response = chart['response'] ?? []; +// +// // Filter the response based on selected filters +// var chartFilteredData = 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']; +// +// // 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 (chartFilteredData.isNotEmpty) { +// filteredData.add({ +// ...chart, // Include all other properties of the chart object +// 'response': chartFilteredData, // Only include filtered response data +// }); +// } +// } +// +// 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']; +// +// // 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 +// }); +// } +// } +// +// // Update the chartsData with the filtered data +// setState(() { +// chartsData = filteredData; +// cardData = filteredCardData; +// }); +// +// print("Filtered Data: $filteredData"); +// print("Filtered Card: $filteredCardData"); +// +// // Go back after applying filters +// Navigator.pop(context); +// } +// +// Widget buildChart(dynamic chartData) { +// // Extract group_by dynamically from the chartData +// String groupByKey = chartData['group_by']; +// +// // Extract unique values for the group_by key dynamically +// Set groupByValues = {}; +// chartData['response'].forEach((entry) { +// groupByValues.add(entry['ObsKey'][groupByKey]); +// }); +// switch (chartData['chart_type']) { +// case 'scatter': +// return SfCartesianChart( +// primaryXAxis: DateTimeAxis(), // Ensure it's DateTimeAxis +// tooltipBehavior: TooltipBehavior(enable: true), +// title: ChartTitle(text: 'Divorces'), +// series: [ +// ScatterSeries( +// dataSource: parseScatterChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => +// data.xDateTime ?? DateTime.now(), +// yValueMapper: (ChartData data, _) => data.y, +// name: chartData['dataset'], +// dataLabelSettings: DataLabelSettings(isVisible: true), +// ), +// ], +// ); +// case 'column': +// return SfCartesianChart( +// primaryXAxis: NumericAxis( +// title: AxisTitle(text: 'Year'), +// interval: 1, // Ensure no fractional intervals +// ), +// title: ChartTitle(text: 'Marriages'), +// series: >[ +// ColumnSeries( +// dataSource: parseColumnChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => data.xInt ?? 0, +// yValueMapper: (ChartData data, _) => data.y, +// name: chartData['dataset'], +// color: Color(0xFF7DAFBC), +// width: 0.8, +// spacing: 0.2, +// dataLabelSettings: +// DataLabelSettings(isVisible: true), // Show data labels +// ), +// ], +// ); +// case 'line': +// return SfCartesianChart( +// primaryXAxis: CategoryAxis(), +// title: ChartTitle(text: 'Line Chart'), +// legend: Legend(isVisible: true), +// tooltipBehavior: TooltipBehavior(enable: true), +// series: >[ +// LineSeries( +// dataSource: parseLineChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => data.timePeriod ?? '', +// yValueMapper: (ChartData data, _) => data.value ?? 0.0, +// name: 'Sales', +// dataLabelSettings: DataLabelSettings(isVisible: true), +// ), +// ], +// ); +// case 'bar': +// return SfCartesianChart( +// primaryXAxis: CategoryAxis(), +// title: ChartTitle(text: 'Bar Chart'), +// tooltipBehavior: TooltipBehavior(enable: true), +// series: >[ +// BarSeries( +// dataSource: parseBarChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => data.timePeriod ?? '', +// yValueMapper: (ChartData data, _) => data.value ?? 0.0, +// name: 'Gold', +// color: Color.fromRGBO(8, 142, 255, 1), +// ), +// ], +// ); +// case 'stacked_bar': +// String chartTitle = +// capitalizeAndSplit(chartData['chart_heading'] ?? ''); +// // Define a list of colors +// final List uniqueColors = [ +// Color(0xFF648CBA), +// Color(0xFF90B0D5), +// Color(0xFF98BCE5), +// Color(0xFFA7B5C5), +// Color(0xFFBED3EC), +// Color(0xFFD4E3F4), +// ]; +// return SfCartesianChart( +// primaryXAxis: CategoryAxis(), +// title: ChartTitle(text: chartData['chart_heading']), +// legend: Legend( +// isVisible: true, +// position: LegendPosition.bottom, +// overflowMode: LegendItemOverflowMode.scroll, +// ), +// tooltipBehavior: TooltipBehavior( +// enable: true, // Enable tooltips +// format: 'point.x : point.y', // Custom tooltip format +// ), +// series: >[ +// for (int i = 0; i < groupByValues.length; i++) +// StackedBarSeries( +// dataSource: parseStackedBarChartData( +// chartData, groupByKey, groupByValues.elementAt(i)), +// xValueMapper: (ChartData data, _) => data.x ?? '', +// yValueMapper: (ChartData data, _) => data.y, +// name: groupByValues.elementAt(i), +// color: uniqueColors[i % uniqueColors.length], +// enableTooltip: true, +// ), +// ], +// ); +// case 'stacked_column': +// String chartTitle = +// capitalizeAndSplit(chartData['chart_heading'] ?? ''); +// // Define a list of colors +// final List uniqueColors = [ +// Color(0xFF648CBA), +// Color(0xFF90B0D5), +// Color(0xFF98BCE5), +// Color(0xFFA7B5C5), +// Color(0xFFBED3EC), +// Color(0xFFD4E3F4), +// ]; +// return SfCartesianChart( +// primaryXAxis: CategoryAxis(), +// title: ChartTitle(text: chartData['chart_heading']), +// legend: Legend( +// isVisible: true, +// position: LegendPosition.bottom, +// overflowMode: LegendItemOverflowMode.scroll, +// ), +// tooltipBehavior: TooltipBehavior( +// enable: true, // Enable tooltips +// format: 'point.x : point.y', // Custom tooltip format +// ), +// series: >[ +// for (int i = 0; i < groupByValues.length; i++) +// StackedColumnSeries( +// dataSource: parseStackedBarChartData( +// chartData, groupByKey, groupByValues.elementAt(i)), +// xValueMapper: (ChartData data, _) => data.x ?? '', +// yValueMapper: (ChartData data, _) => data.y, +// name: groupByValues.elementAt(i), +// color: uniqueColors[i % uniqueColors.length], +// enableTooltip: true, +// ), +// ], +// ); +// default: +// return Center(child: Text('Unknown chart type')); +// } +// } +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// key: _scaffoldKey, +// backgroundColor: Colors.brown[200]!, +// appBar: AppBar( +// backgroundColor: Colors.brown[200]!, +// elevation: 0, +// leading: IconButton( +// icon: Icon(Icons.arrow_back_ios_new, color: Colors.white), +// onPressed: () { +// context.go('/myhomepage'); +// }, +// ), +// title: Text( +// 'UAE Numbers', +// style: TextStyle(color: Colors.white), +// ), +// ), +// body: isLoading +// ? Center(child: CircularProgressIndicator()) +// : Column( +// children: [ +// Column( +// crossAxisAlignment: CrossAxisAlignment.end, +// children: [ +// Row( +// mainAxisAlignment: MainAxisAlignment +// .end, // Aligns the row's content to the right +// children: [ +// Row( +// children: [ +// Text( +// 'Bookmark', +// style: const TextStyle( +// fontSize: 16, +// color: Colors.white, +// ), +// ), +// Icon(Icons.bookmark_add_outlined, +// color: Colors.white, size: 18), +// ], +// ), +// SizedBox(width: 10), // Horizontal space between items +// Row( +// children: [ +// Text( +// 'Share', +// style: const TextStyle( +// fontSize: 16, +// color: Colors.white, +// ), +// ), +// Icon(Icons.share_sharp, +// color: Colors.white, size: 18), +// ], +// ), +// SizedBox(width: 10), // Horizontal space between items +// IconButton( +// icon: Icon(Icons.filter_alt_outlined, +// color: Colors.white, size: 18), +// onPressed: () { +// showRightSideModal( +// context, chartsData[0]["filters"], chartsData); +// }, +// ), +// ], +// ), +// ], +// ), +// +// // Padding( +// // padding: const EdgeInsets.all(16.0), +// // child: Align( +// // alignment: Alignment.topRight, +// // child: IconButton( +// // icon: Icon(Icons.filter_list, color: Colors.white), +// // onPressed: () { +// // showRightSideModal( +// // context, chartsData[0]["filters"], chartsData); +// // }, +// // ), +// // ), +// // ), +// Expanded( +// child: ListView( +// children: [ +// // nonChartData Cards +// GridView.builder( +// itemCount: cardData.length, +// shrinkWrap: true, +// physics: const NeverScrollableScrollPhysics(), +// gridDelegate: +// const SliverGridDelegateWithFixedCrossAxisCount( +// crossAxisCount: 2, // 2 cards per row +// crossAxisSpacing: 10, +// mainAxisSpacing: 10, +// childAspectRatio: 177.5 / +// 180, // Adjusted to maintain width and fixed height +// ), +// 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 data = processNonChartData(item); +// print('processNonChartData'); +// +// if (chart_type == 'total') { +// return Card( +// margin: const EdgeInsets.all(10), +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(12), +// ), +// elevation: 4, +// child: Padding( +// padding: const EdgeInsets.all(12.0), +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// const Icon(Icons.public, +// color: Color(0xFF90B0D5), size: 22), +// const SizedBox(height: 5), +// Text( +// '${chart_heading ?? 'NA'}', +// textAlign: TextAlign.center, +// style: TextStyle( +// fontSize: 14, +// fontWeight: FontWeight.bold, +// color: Colors.black87, +// ), +// ), +// Text( +// '(${data['lastYear'] ?? 'NA'})', +// style: const TextStyle( +// fontSize: 10, color: Colors.grey), +// ), +// Text( +// '${data['lastYearValue'] ?? 'NA'}', +// style: const TextStyle( +// fontSize: 16, +// fontWeight: FontWeight.bold, +// color: Color(0xFF90B0D5), +// ), +// ), +// const Divider( +// color: Colors.grey, thickness: 1), +// Text( +// '${data['secondLastYearValue'] ?? 'NA'}', +// style: const TextStyle( +// fontSize: 14, +// fontWeight: FontWeight.bold, +// color: Color(0xFF90B0D5), +// ), +// ), +// Text( +// '(${data['secondLastYear'] ?? 'NA'})', +// style: const TextStyle( +// fontSize: 10, color: Colors.grey), +// ), +// ], +// ), +// ), +// ); +// } else if (chart_type == 'average') { +// return Card( +// margin: const EdgeInsets.all(10), +// shape: RoundedRectangleBorder( +// borderRadius: BorderRadius.circular(12), +// ), +// elevation: 4, +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// mainAxisAlignment: MainAxisAlignment.center, +// children: [ +// const Icon(Icons.analytics, +// color: Color(0xFF90B0D5), size: 22), +// const SizedBox(height: 5), +// Text( +// '${chart_heading ?? 'NA'}', +// textAlign: TextAlign.center, +// style: TextStyle( +// fontSize: 14, +// fontWeight: FontWeight.bold, +// color: Colors.black87, +// ), +// ), +// Text( +// '(${data['firstYear'] ?? 'NA'} - ${data['lastYear'] ?? 'NA'})', +// style: const TextStyle( +// fontSize: 10, color: Colors.grey), +// ), +// Text( +// '${data['roundedAverage'] ?? 'NA'}', +// style: const TextStyle( +// fontSize: 16, +// fontWeight: FontWeight.bold, +// color: Color(0xFF90B0D5), +// ), +// ), +// ], +// ), +// ), +// ); +// } else { +// return const SizedBox +// .shrink(); // Ignore unknown KPIs +// } +// }, +// ), +// // chartsData Cards +// ListView.builder( +// itemCount: chartsData.length, +// shrinkWrap: true, +// physics: const NeverScrollableScrollPhysics(), +// itemBuilder: (context, index) { +// return Card( +// margin: const EdgeInsets.all(10), +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// const SizedBox(height: 10), +// Container( +// height: 300, +// child: buildChart(chartsData[index]), +// ), +// ], +// ), +// ), +// ); +// }, +// ), +// ], +// ), +// ), +// ], +// ), +// ); +// } +// +// void showRightSideModal(BuildContext context, List filters, List data) { +// // Initialize selected filters structure from the stored value, if exists +// List> selectedFilters = +// selectedFiltersStorage.isNotEmpty +// ? List.from(selectedFiltersStorage) // Use stored filters +// : filters.map((filter) { +// return {"filter_key": filter["filter_key"], "filter_data": []}; +// }).toList(); // Or initialize empty filters +// +// showModalBottomSheet( +// context: context, +// isScrollControlled: true, +// backgroundColor: Colors.transparent, +// builder: (BuildContext context) { +// return Align( +// alignment: Alignment.centerRight, +// child: Container( +// width: MediaQuery.of(context).size.width * 0.7, +// height: MediaQuery.of(context).size.height, +// color: Colors.white, +// child: Column( +// children: [ +// // Header Section +// Padding( +// padding: const EdgeInsets.all(16.0), +// child: Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// Text( +// 'Filters', +// style: TextStyle( +// fontSize: 18, +// fontWeight: FontWeight.bold, +// ), +// ), +// IconButton( +// icon: Icon(Icons.close), +// onPressed: () => Navigator.pop(context), +// ), +// ], +// ), +// ), +// // Filters Section +// Expanded( +// child: ListView.builder( +// itemCount: filters.length, +// itemBuilder: (context, index) { +// final filter = filters[index]; +// final filterKey = filter["filter_key"]; +// final filterData = filter["filter_data"]; +// +// return StatefulBuilder( +// builder: (context, setState) { +// // Get the corresponding selected filter object +// var selectedFilter = selectedFilters +// .firstWhere((f) => f["filter_key"] == filterKey); +// +// return Padding( +// padding: const EdgeInsets.symmetric( +// horizontal: 16.0, vertical: 8.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// filterKey, +// style: TextStyle( +// fontSize: 16, +// fontWeight: FontWeight.bold, +// ), +// ), +// InkWell( +// onTap: () { +// showDialog( +// context: context, +// builder: (BuildContext context) { +// return StatefulBuilder( +// builder: (context, dialogSetState) { +// return AlertDialog( +// title: Text('Select $filterKey'), +// content: SingleChildScrollView( +// child: ListBody( +// children: filterData +// .map((value) { +// return CheckboxListTile( +// title: Text( +// value.toString()), +// value: selectedFilter[ +// "filter_data"] +// .contains(value), +// onChanged: +// (bool? isChecked) { +// dialogSetState(() { +// if (isChecked == +// true) { +// selectedFilter[ +// "filter_data"] +// .add(value); +// } else { +// selectedFilter[ +// "filter_data"] +// .remove(value); +// } +// }); +// }, +// ); +// }).toList(), +// ), +// ), +// actions: [ +// TextButton( +// onPressed: () { +// // Store the updated selected filters after dialog closes +// setState(() {}); +// Navigator.pop(context); +// }, +// child: Text('OK'), +// ), +// ], +// ); +// }, +// ); +// }, +// ); +// }, +// child: Container( +// width: double.infinity, +// padding: EdgeInsets.symmetric( +// horizontal: 16.0, vertical: 12.0), +// decoration: BoxDecoration( +// border: Border.all(color: Colors.grey), +// borderRadius: BorderRadius.circular(8.0), +// ), +// child: Wrap( +// spacing: 8.0, +// runSpacing: 4.0, +// children: +// selectedFilter["filter_data"].isEmpty +// ? [ +// Text('Select $filterKey', +// style: TextStyle( +// color: Colors.grey)) +// ] +// : selectedFilter["filter_data"] +// .map((value) { +// return Chip( +// label: Text(value), +// onDeleted: () { +// setState(() { +// selectedFilter[ +// "filter_data"] +// .remove(value); +// }); +// }, +// ); +// }).toList(), +// ), +// ), +// ), +// ], +// ), +// ); +// }, +// ); +// }, +// ), +// ), +// // Button Section +// Padding( +// padding: const EdgeInsets.all(16.0), +// child: Row( +// mainAxisAlignment: MainAxisAlignment.spaceBetween, +// children: [ +// ElevatedButton( +// onPressed: () { +// // Clear all selected filters +// selectedFilters.forEach((filter) { +// filter["filter_data"].clear(); +// }); +// setState(() { +// chartsData = List.from(originalChartsData); +// cardData = List.from(originalCardData); +// }); +// // Reset the selectedFiltersStorage to empty when clearing +// selectedFiltersStorage.clear(); +// Navigator.pop(context); +// }, +// child: Text('Clear'), +// style: ElevatedButton.styleFrom( +// backgroundColor: Colors.grey, +// ), +// ), +// ElevatedButton( +// onPressed: () { +// print( +// "Selected Filters before applying: $selectedFilters"); +// setState(() { +// chartsData = originalChartsData; +// cardData = originalCardData; +// }); +// applyFilters(context, filters, chartsData, cardData, +// selectedFilters); +// // Save the selected filters to storage after applying +// selectedFiltersStorage = List.from(selectedFilters); +// }, +// child: Text('Apply Filter'), +// style: ElevatedButton.styleFrom( +// backgroundColor: Colors.blue, +// ), +// ), +// ], +// ), +// ), +// ], +// ), +// ), +// ); +// }, +// ); +// } +// } +// +// class ChartData { +// final int? xInt; // For column and other charts, using int for x +// final DateTime? xDateTime; // For scatter charts, using DateTime for x +// final double? y; // For column and other charts +// final String? timePeriod; // For line and bar charts +// final double? value; // For line and bar charts +// late final double? y2; // For secondary data (stacked column chart) +// late final double? y3; // For tertiary data (stacked column chart) +// final double? y4; // For quaternary data (stacked column chart) +// final String? x; // For stacked bar charts, using String for x +// +// // Constructor that handles all chart cases +// ChartData({ +// this.xInt, +// this.xDateTime, +// this.y, +// this.timePeriod, +// this.value, +// this.y2, +// this.y3, +// this.y4, +// this.x, +// }); +// } +// +[ +{ +chart_heading :"Guest Nights by Region" +chart_type :"fl_stacked_bar" +dataset :"hotels" +group_by :"GUEST_REGION" +is_chart :"true" +kpi :"hotel_occupancy_rate_chart_1" +main_id :"economy" +response:[ +{ +"ObsKey": { +"FREQ": "A", +"GUEST_REGION": "AF", +"H_INDICATOR": "GUN", +"H_TYPE": "_Z", +"MEASURE": "H", +"REF_AREA": "AE", +"SOURCE_DETAIL": "FCSC", +"TIME_PERIOD": "2016", +"UNIT_MEASURE": "NUMBER" +}, +"ObsValue": { +"Value": "2659446" +} +}, +{ +"ObsKey": { +"FREQ": "A", +"GUEST_REGION": "OC", +"H_INDICATOR": "GUN", +"H_TYPE": "_Z", +"MEASURE": "H", +"REF_AREA": "AE", +"SOURCE_DETAIL": "FCSC", +"TIME_PERIOD": "2018", +"UNIT_MEASURE": "NUMBER" +}, +"ObsValue": { +"Value": "1127282" +} +} + +] +sub_id :"tourism" +url :"https://releaseeuaestat.fcsc.gov.ae/rest/data/FCSA,DF_GUEST_REGION,4.3.0/...A..GUN.OTH+OC+AF+EC+AM+AC+ASC+GCC+UAE.?startPeriod=2016&dimensionAtObservation=AllDimensions" +} +] diff --git a/lib/presentation/Screens/charts/screens/chart_screen.dart b/lib/presentation/Screens/charts/screens/chart_screen.dart index 0237b800..360e9d7a 100644 --- a/lib/presentation/Screens/charts/screens/chart_screen.dart +++ b/lib/presentation/Screens/charts/screens/chart_screen.dart @@ -7,7 +7,10 @@ import '../filters/search_filter_helper.dart'; class ChartScreen1 extends StatefulWidget { final String dataSets; - const ChartScreen1({Key? key, required this.dataSets}); + final String bgColor; + + const ChartScreen1( + {Key? key, required this.dataSets, required String this.bgColor}); @override _ChartScreen1State createState() => _ChartScreen1State(); } @@ -24,10 +27,13 @@ class _ChartScreen1State extends State { List cardData = []; List originalChartsData = []; List originalCardData = []; + late Color backgroundColor; @override void initState() { super.initState(); + final String bgColor = widget.bgColor; + print(' bgColor $bgColor'); fetchChartData(widget.dataSets); } @@ -368,11 +374,13 @@ class _ChartScreen1State extends State { @override Widget build(BuildContext context) { + final color = + Color(int.parse(widget.bgColor.replaceFirst('0x', ''), radix: 16)); return Scaffold( key: _scaffoldKey, - backgroundColor: Colors.brown[200]!, + backgroundColor: color, appBar: AppBar( - backgroundColor: Colors.brown[200]!, + backgroundColor: color, elevation: 0, leading: IconButton( icon: Icon(Icons.arrow_back_ios_new, color: Colors.white), @@ -451,8 +459,8 @@ class _ChartScreen1State extends State { crossAxisCount: 2, // 2 cards per row crossAxisSpacing: 10, mainAxisSpacing: 10, - childAspectRatio: 177.5 / - 180, // Adjusted to maintain width and fixed height + childAspectRatio: 190.5 / + 200, // Adjusted to maintain width and fixed height ), itemBuilder: (context, index) { final item = cardData[index]; @@ -470,49 +478,56 @@ class _ChartScreen1State extends State { ), elevation: 4, child: Padding( - padding: const EdgeInsets.all(12.0), + padding: const EdgeInsets.all(10.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.public, - color: Color(0xFF90B0D5), size: 22), - const SizedBox(height: 5), + color: Color(0xFF90B0D5), size: 30), + const SizedBox(height: 3), Text( '${chart_heading ?? 'NA'}', textAlign: TextAlign.center, style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, + fontSize: 11, + fontWeight: FontWeight.w400, color: Colors.black87, ), ), Text( '(${data['lastYear'] ?? 'NA'})', style: const TextStyle( - fontSize: 10, color: Colors.grey), + fontSize: 11, color: Colors.grey), ), Text( - '${data['lastYearValue'] ?? 'NA'}', + apiService + .formatAmount(data['lastYearValue']), style: const TextStyle( - fontSize: 16, + fontSize: 20, fontWeight: FontWeight.bold, color: Color(0xFF90B0D5), ), ), - const Divider( - color: Colors.grey, thickness: 1), + SizedBox( + height: 5, // Height of the divider + child: Divider( + color: Colors.grey, + thickness: 1, // Divider line thickness + ), + ), Text( - '${data['secondLastYearValue'] ?? 'NA'}', + apiService.formatAmount( + data['secondLastYearValue']), style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: Color(0xFF90B0D5), + fontSize: 16, + fontWeight: FontWeight.w600, + color: Color(0xFFD83731), ), ), Text( '(${data['secondLastYear'] ?? 'NA'})', style: const TextStyle( - fontSize: 10, color: Colors.grey), + fontSize: 11, color: Colors.grey), ), ], ), @@ -531,28 +546,28 @@ class _ChartScreen1State extends State { mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.analytics, - color: Color(0xFF90B0D5), size: 22), + color: Color(0xFF90B0D5), size: 30), const SizedBox(height: 5), Text( '${chart_heading ?? 'NA'}', textAlign: TextAlign.center, style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, + fontSize: 11, + fontWeight: FontWeight.w400, color: Colors.black87, ), ), Text( '(${data['firstYear'] ?? 'NA'} - ${data['lastYear'] ?? 'NA'})', style: const TextStyle( - fontSize: 10, color: Colors.grey), + fontSize: 11, color: Colors.grey), ), Text( '${data['roundedAverage'] ?? 'NA'}', style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Color(0xFF90B0D5), + fontSize: 26, + fontWeight: FontWeight.w900, + color: Color(0xFF11AF22), ), ), ], diff --git a/lib/presentation/Screens/charts/services/api_service.dart b/lib/presentation/Screens/charts/services/api_service.dart index 02952e20..31494a16 100644 --- a/lib/presentation/Screens/charts/services/api_service.dart +++ b/lib/presentation/Screens/charts/services/api_service.dart @@ -56,45 +56,6 @@ class ApiService { } } - /// Fetch chart data and categorize it into chart and non-chart data - // Future fetchChartData(String dataSets) async { - // final url = Uri.parse('$baseUrl?dataset=$dataSets'); - // - // List isChartData = []; - // List nonChartData = []; - // List originalChartsData = []; - // List originalCardData = []; - // - // try { - // final response = await http.get(url); - // - // if (response.statusCode == 200) { - // final List data = jsonDecode(response.body); - // - // for (var item in data) { - // if (item['is_chart'] == 'true') { - // isChartData.add(item); - // } else if (item['is_chart'] == 'false') { - // nonChartData.add(item); - // } - // } - // - // print('Chart Data: $isChartData'); - // print('Non-Chart Data: $nonChartData'); - // - // originalChartsData = List.from(isChartData); - // originalCardData = List.from(nonChartData); - // - // print('chartsData $isChartData'); - // print('cardData $nonChartData'); - // } else { - // throw Exception('Failed to load data'); - // } - // } catch (error) { - // print('Error fetching data: $error'); - // } - // } - /// Process non-chart data by extracting and calculating values Map processNonChartData(dynamic item) { print('Processing non-chart data...'); @@ -181,4 +142,22 @@ class ApiService { ); }).toList(); } + + String formatAmount(dynamic value) { + // Handle null or non-numeric values + if (value == null || num.tryParse(value.toString()) == null) { + return 'NA'; + } + + num amount = num.tryParse(value.toString())!; + + // Check if the amount is in millions or thousands + if (amount >= 1000000) { + return '${(amount / 1000000).toStringAsFixed(2)}M'; + } else if (amount >= 1000) { + return '${(amount / 1000).toStringAsFixed(2)}K'; + } else { + return '$amount'; + } + } } diff --git a/lib/presentation/Screens/charts/widgets/chart_widget.dart b/lib/presentation/Screens/charts/widgets/chart_widget.dart index 4e7c4836..ddff96f3 100644 --- a/lib/presentation/Screens/charts/widgets/chart_widget.dart +++ b/lib/presentation/Screens/charts/widgets/chart_widget.dart @@ -2,7 +2,7 @@ import 'dart:math'; import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; -import 'package:syncfusion_flutter_charts/charts.dart'; +// import 'package:syncfusion_flutter_charts/charts.dart'; class ChartWidget extends StatelessWidget { final dynamic chartData; @@ -121,80 +121,80 @@ class ChartWidget extends StatelessWidget { print('groupByValues $groupByValues'); switch (chartData['chart_type']) { - case 'stacked_bar': - String chartTitle = - capitalizeAndSplit(chartData['chart_heading'] ?? ''); - // Define a list of colors - final List uniqueColors = [ - Color(0xFF648CBA), - Color(0xFF90B0D5), - Color(0xFF98BCE5), - Color(0xFFA7B5C5), - Color(0xFFBED3EC), - Color(0xFFD4E3F4), - ]; - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: chartData['chart_heading']), - legend: Legend( - isVisible: true, - position: LegendPosition.bottom, - overflowMode: LegendItemOverflowMode.scroll, - ), - tooltipBehavior: TooltipBehavior( - enable: true, // Enable tooltips - format: 'point.x : point.y', // Custom tooltip format - ), - series: >[ - for (int i = 0; i < groupByValues.length; i++) - StackedBarSeries( - dataSource: parseStackedBarChartData( - chartData, groupByKey, groupByValues.elementAt(i)), - xValueMapper: (ChartData data, _) => data.x ?? '', - yValueMapper: (ChartData data, _) => data.y, - name: groupByValues.elementAt(i), - color: uniqueColors[i % uniqueColors.length], - enableTooltip: true, - ), - ], - ); - case 'stacked_column': - String chartTitle = - capitalizeAndSplit(chartData['chart_heading'] ?? ''); - // Define a list of colors - final List uniqueColors = [ - Color(0xFF648CBA), - Color(0xFF90B0D5), - Color(0xFF98BCE5), - Color(0xFFA7B5C5), - Color(0xFFBED3EC), - Color(0xFFD4E3F4), - ]; - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: chartData['chart_heading']), - legend: Legend( - isVisible: true, - position: LegendPosition.bottom, - overflowMode: LegendItemOverflowMode.scroll, - ), - tooltipBehavior: TooltipBehavior( - enable: true, // Enable tooltips - format: 'point.x : point.y', // Custom tooltip format - ), - series: >[ - for (int i = 0; i < groupByValues.length; i++) - StackedColumnSeries( - dataSource: parseStackedBarChartData( - chartData, groupByKey, groupByValues.elementAt(i)), - xValueMapper: (ChartData data, _) => data.x ?? '', - yValueMapper: (ChartData data, _) => data.y, - name: groupByValues.elementAt(i), - color: uniqueColors[i % uniqueColors.length], - enableTooltip: true, - ), - ], - ); + // case 'stacked_bar': + // String chartTitle = + // capitalizeAndSplit(chartData['chart_heading'] ?? ''); + // // Define a list of colors + // final List uniqueColors = [ + // Color(0xFF648CBA), + // Color(0xFF90B0D5), + // Color(0xFF98BCE5), + // Color(0xFFA7B5C5), + // Color(0xFFBED3EC), + // Color(0xFFD4E3F4), + // ]; + // return SfCartesianChart( + // primaryXAxis: CategoryAxis(), + // title: ChartTitle(text: chartData['chart_heading']), + // legend: Legend( + // isVisible: true, + // position: LegendPosition.bottom, + // overflowMode: LegendItemOverflowMode.scroll, + // ), + // tooltipBehavior: TooltipBehavior( + // enable: true, // Enable tooltips + // format: 'point.x : point.y', // Custom tooltip format + // ), + // series: >[ + // for (int i = 0; i < groupByValues.length; i++) + // StackedBarSeries( + // dataSource: parseStackedBarChartData( + // chartData, groupByKey, groupByValues.elementAt(i)), + // xValueMapper: (ChartData data, _) => data.x ?? '', + // yValueMapper: (ChartData data, _) => data.y, + // name: groupByValues.elementAt(i), + // color: uniqueColors[i % uniqueColors.length], + // enableTooltip: true, + // ), + // ], + // ); + // case 'stacked_column': + // String chartTitle = + // capitalizeAndSplit(chartData['chart_heading'] ?? ''); + // // Define a list of colors + // final List uniqueColors = [ + // Color(0xFF648CBA), + // Color(0xFF90B0D5), + // Color(0xFF98BCE5), + // Color(0xFFA7B5C5), + // Color(0xFFBED3EC), + // Color(0xFFD4E3F4), + // ]; + // return SfCartesianChart( + // primaryXAxis: CategoryAxis(), + // title: ChartTitle(text: chartData['chart_heading']), + // legend: Legend( + // isVisible: true, + // position: LegendPosition.bottom, + // overflowMode: LegendItemOverflowMode.scroll, + // ), + // tooltipBehavior: TooltipBehavior( + // enable: true, // Enable tooltips + // format: 'point.x : point.y', // Custom tooltip format + // ), + // series: >[ + // for (int i = 0; i < groupByValues.length; i++) + // StackedColumnSeries( + // dataSource: parseStackedBarChartData( + // chartData, groupByKey, groupByValues.elementAt(i)), + // xValueMapper: (ChartData data, _) => data.x ?? '', + // yValueMapper: (ChartData data, _) => data.y, + // name: groupByValues.elementAt(i), + // color: uniqueColors[i % uniqueColors.length], + // enableTooltip: true, + // ), + // ], + // ); case 'column_chart': // Case 3 for fl_chart column chart return BarChart( BarChartData( @@ -369,42 +369,37 @@ class ChartWidget extends StatelessWidget { Padding( padding: const EdgeInsets.all(5.0), child: Wrap( - spacing: 5, - runSpacing: 5, - children: chunkedGroupByValues.map((chunk) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: chunk.map((group) { - List groupByValuesList = groupByValues.toList(); - int index = groupByValuesList.indexOf(group); - Color groupColor = _getColorForGroup(index); - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 5.0), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - width: 10, - height: 10, - decoration: BoxDecoration( - color: groupColor, - shape: BoxShape.circle, - ), - ), - SizedBox(width: 3), - Text( - group, - overflow: TextOverflow.ellipsis, - style: TextStyle(fontSize: 11), - ), - ], + spacing: 10, // Horizontal spacing between items + runSpacing: 5, // Vertical spacing between rows + children: chunkedGroupByValues.expand((chunk) { + return chunk.map((group) { + List groupByValuesList = groupByValues.toList(); + int index = groupByValuesList.indexOf(group); + Color groupColor = _getColorForGroup(index); + + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Container( + width: 10, + height: 10, + decoration: BoxDecoration( + color: groupColor, + shape: BoxShape.circle, + ), ), - ); - }).toList(), - ); + SizedBox(width: 5), + Text( + group, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 11), + ), + ], + ); + }).toList(); }).toList(), ), - ), + ) ], ); case 'fl_multi_bar': diff --git a/lib/presentation/Screens/demo_home.dart b/lib/presentation/Screens/demo_home.dart index 895a5a29..bbfc6f7c 100644 --- a/lib/presentation/Screens/demo_home.dart +++ b/lib/presentation/Screens/demo_home.dart @@ -1,424 +1,424 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:intl/intl.dart'; -import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package -import 'package:http/http.dart' as http; - -void main() { - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - home: ChartPage(dataSets: ''), - ); - } -} - -class ChartPage extends StatefulWidget { - final String dataSets; - const ChartPage({Key? key, required this.dataSets}); - - @override - ChartPageState createState() => ChartPageState(); -} - -class ChartPageState extends State { - List chartsData = []; - bool isLoading = true; - - @override - void initState() { - super.initState(); - fetchChartData(widget.dataSets); - } - - Future fetchChartData(datasets) async { - const baseUrl = - 'https://pb.venbait.in/api/custom/apicall'; // Replace with your server URL - final url = Uri.parse('$baseUrl?dataset=$datasets'); - - try { - final response = await http.get(url); - if (response.statusCode == 200) { - setState(() { - chartsData = jsonDecode(response.body); - isLoading = false; - }); - } else { - throw Exception('Failed to load data'); - } - } catch (error) { - setState(() { - isLoading = false; - }); - print('Error fetching data: $error'); - } - } - - List parseLineChartData(List response) { - // Group by timePeriod (year) and sum the values - Map groupedData = {}; - - response.forEach((entry) { - final year = entry['ObsKey']['TIME_PERIOD']?.toString() ?? 'Unknown'; - final value = - double.tryParse(entry['ObsValue']['Value']?.toString() ?? '0.0') ?? - 0.0; - - // Add the value to the existing year (if exists) or initialize it - if (groupedData.containsKey(year)) { - groupedData[year] = groupedData[year]! + value; - } else { - groupedData[year] = value; - } - }); - - // Sort the grouped data by year in ascending order - var sortedEntries = groupedData.entries.toList() - ..sort((a, b) => int.parse(a.key).compareTo(int.parse(b.key))); - - // Convert the sorted data to a list of ChartData - return sortedEntries - .map((entry) => ChartData( - timePeriod: entry.key, - value: entry.value, - value1: 0.0)) // Add value1 as 0.0 - .toList(); - } - - List parseBarChartData(List response) { - return response - .asMap() - .entries - .map((entry) => ChartData( - timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ?? 'Unknown', - value: double.tryParse( - entry.value['ObsValue']['Value'].toString()) ?? - 0.0, - value1: 0.0, // Ensure value1 is passed as well - )) - .toList(); - } - - List parseColumnChartData(List response) { - List chartDataList = []; - - // Temporary map to store values by TIME_PERIOD - Map timePeriodMap = {}; - - for (var entry in response) { - String timePeriod = entry['ObsKey']['TIME_PERIOD'].toString(); - double value = - double.tryParse(entry['ObsValue']['Value']?.toString() ?? '0.0') ?? - 0.0; - String indicator = entry['ObsKey']['H_INDICATOR']; - - // If the entry is for TOR, map it to 'value' - if (indicator == 'TOR') { - if (timePeriodMap.containsKey(timePeriod)) { - // Update the value by creating a new ChartData object - timePeriodMap[timePeriod] = ChartData( - timePeriod: timePeriod, - value: value, // Set the new value - value1: timePeriodMap[timePeriod]?.value1 ?? 0.0, - ); - } else { - timePeriodMap[timePeriod] = ChartData( - timePeriod: timePeriod, - value: value, - value1: 0.0, - ); - } - } - - // If the entry is for TAR, map it to 'value1' - if (indicator == 'TAR') { - if (timePeriodMap.containsKey(timePeriod)) { - // Create a new ChartData object with the updated value1 - timePeriodMap[timePeriod] = ChartData( - timePeriod: timePeriod, - value: timePeriodMap[timePeriod]?.value ?? - 0.0, // Retain the current value - value1: value, // Update the value1 - ); - } else { - timePeriodMap[timePeriod] = ChartData( - timePeriod: timePeriod, - value: 0.0, - value1: value, - ); - } - } - } - - // Convert the map to a list - chartDataList = timePeriodMap.values.toList(); - - return chartDataList; - } - - Widget buildChart(dynamic chartData) { - print('chartData $chartData'); - switch (chartData['chart_type']) { - case 'area': - return SfCartesianChart( - primaryXAxis: CategoryAxis( - title: AxisTitle(text: 'Year'), - labelRotation: 45, // Rotate labels if they overlap - ), - title: ChartTitle(text: 'Hotel Estabhlishments'), - legend: Legend(isVisible: true), - tooltipBehavior: TooltipBehavior( - enable: true, - builder: (dynamic data, dynamic point, dynamic series, - int pointIndex, int seriesIndex) { - return Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5), - ), - child: Text( - '${data.timePeriod}: ${formatNumber(data.value)}', - style: const TextStyle(color: Colors.black), - ), - ); - }, - ), - series: [ - // AreaSeries for the area chart - AreaSeries( - dataSource: parseLineChartData( - chartData['response']), // your chart data - color: Color(0xFF7DAFBC), // Area color - borderDrawMode: BorderDrawMode - .excludeBottom, // Optional, adjust border settings - borderColor: Colors.green, // Border color - borderWidth: 2, // Border width - xValueMapper: (ChartData data, _) => - data.timePeriod, // Map x to DateTime - yValueMapper: (ChartData data, _) => data.value, - // name: chartData['dataset'], - name: 'Hotels', - dataLabelSettings: DataLabelSettings( - isVisible: true, - builder: (dynamic data, dynamic point, dynamic series, - int pointIndex, int seriesIndex) { - return Text( - formatNumber(data.value), - style: const TextStyle(fontSize: 12, color: Colors.black), - ); - }, - ), // Map y to numerical value - ) - ]); - case 'bar': - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: 'Hotel Occupancy Rate'), - tooltipBehavior: TooltipBehavior( - enable: true, - builder: (dynamic data, dynamic point, dynamic series, - int pointIndex, int seriesIndex) { - return Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(5), - ), - child: Text( - '${data.timePeriod}: ${formatNumber(data.value)}', - style: const TextStyle(color: Colors.black), - ), - ); - }, - ), - series: >[ - BarSeries( - dataSource: parseBarChartData(chartData['response']), - xValueMapper: (ChartData data, _) => data.timePeriod, - yValueMapper: (ChartData data, _) => data.value, - name: chartData['dataset'], - color: Color(0xFF7DAFBC), - dataLabelSettings: DataLabelSettings( - isVisible: true, - builder: (dynamic data, dynamic point, dynamic series, - int pointIndex, int seriesIndex) { - return Text( - formatNumber(data.value), - style: const TextStyle(fontSize: 12, color: Colors.black), - ); - }, - ), - // isTrackVisible: true, - // trackColor: Colors.red - ), - ], - ); - case 'column': - return SfCartesianChart( - primaryXAxis: CategoryAxis( - title: AxisTitle(text: 'Year'), - labelRotation: 45, // Rotate labels if they overlap - ), - primaryYAxis: NumericAxis( - title: AxisTitle(text: 'No of rooms'), - ), - legend: Legend(isVisible: true), - title: ChartTitle(text: 'Hotel Occupancy Rate'), - enableSideBySideSeriesPlacement: true, - tooltipBehavior: TooltipBehavior(enable: true), - series: >[ - // First Series (value) - - ColumnSeries( - name: 'Available Rooms', - // opacity: 0.9, - // width: 0.4, - color: Color(0xFF587BA3), - dataSource: parseColumnChartData(chartData['response']), - xValueMapper: (ChartData data, _) => int.parse(data.timePeriod), - yValueMapper: (ChartData data, _) => data.value1, - ), - // Second Series (value1) - ColumnSeries( - color: Color(0xFF90B0D5), - name: 'Occupied Rooms', - dataSource: parseColumnChartData(chartData['response']), - xValueMapper: (ChartData data, _) => int.parse(data.timePeriod), - yValueMapper: (ChartData data, _) => data.value, - ), - ], // Numeric Y-axis to plot the values - ); - default: - return Center(child: Text('Unknown chart type')); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: const Color(0xFF7DAFBC), - appBar: AppBar( - backgroundColor: const Color(0xFF7DAFBC), - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white), - onPressed: () { - context.go('/myhomepage'); - }, - ), - title: const Text( - 'Charts', - style: TextStyle(color: Colors.white), - ), - ), - body: isLoading - ? const Center(child: CircularProgressIndicator()) - : Column( - children: [ - Card( - margin: const EdgeInsets.all(10), - color: Colors.white, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - Text( - 'Card 1 Title', - style: TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), - ), - SizedBox(height: 10), - Text('Content for the first card goes here.'), - ], - ), - ), - ), - Card( - margin: const EdgeInsets.all(10), - color: Colors.white, - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: const [ - Text( - 'Card 2 Title', - style: TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), - ), - SizedBox(height: 10), - Text('Content for the second card goes here.'), - ], - ), - ), - ), - Expanded( - child: ListView.builder( - itemCount: chartsData.length, - itemBuilder: (context, index) { - return Card( - margin: const EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: 300, - child: buildChart(chartsData[index]), - ), - ], - ), - ), - ); - }, - ), - ), - ], - ), - ); - } -} - -class ChartData { - final String timePeriod; - final double value; - final double value1; - final String formattedValue; - - ChartData( - {required this.timePeriod, required this.value, required this.value1}) - : formattedValue = formatNumber(value); -} - +// import 'dart:convert'; +// import 'package:flutter/material.dart'; +// import 'package:go_router/go_router.dart'; +// import 'package:intl/intl.dart'; +// import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package +// import 'package:http/http.dart' as http; +// +// void main() { +// runApp(MyApp()); +// } +// +// class MyApp extends StatelessWidget { +// @override +// Widget build(BuildContext context) { +// return MaterialApp( +// home: ChartPage(dataSets: ''), +// ); +// } +// } +// +// class ChartPage extends StatefulWidget { +// final String dataSets; +// const ChartPage({Key? key, required this.dataSets}); +// +// @override +// ChartPageState createState() => ChartPageState(); +// } +// +// class ChartPageState extends State { +// List chartsData = []; +// bool isLoading = true; +// +// @override +// void initState() { +// super.initState(); +// fetchChartData(widget.dataSets); +// } +// +// Future fetchChartData(datasets) async { +// const baseUrl = +// 'https://pb.venbait.in/api/custom/apicall'; // Replace with your server URL +// final url = Uri.parse('$baseUrl?dataset=$datasets'); +// +// try { +// final response = await http.get(url); +// if (response.statusCode == 200) { +// setState(() { +// chartsData = jsonDecode(response.body); +// isLoading = false; +// }); +// } else { +// throw Exception('Failed to load data'); +// } +// } catch (error) { +// setState(() { +// isLoading = false; +// }); +// print('Error fetching data: $error'); +// } +// } +// +// List parseLineChartData(List response) { +// // Group by timePeriod (year) and sum the values +// Map groupedData = {}; +// +// response.forEach((entry) { +// final year = entry['ObsKey']['TIME_PERIOD']?.toString() ?? 'Unknown'; +// final value = +// double.tryParse(entry['ObsValue']['Value']?.toString() ?? '0.0') ?? +// 0.0; +// +// // Add the value to the existing year (if exists) or initialize it +// if (groupedData.containsKey(year)) { +// groupedData[year] = groupedData[year]! + value; +// } else { +// groupedData[year] = value; +// } +// }); +// +// // Sort the grouped data by year in ascending order +// var sortedEntries = groupedData.entries.toList() +// ..sort((a, b) => int.parse(a.key).compareTo(int.parse(b.key))); +// +// // Convert the sorted data to a list of ChartData +// return sortedEntries +// .map((entry) => ChartData( +// timePeriod: entry.key, +// value: entry.value, +// value1: 0.0)) // Add value1 as 0.0 +// .toList(); +// } +// +// List parseBarChartData(List response) { +// return response +// .asMap() +// .entries +// .map((entry) => ChartData( +// timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ?? 'Unknown', +// value: double.tryParse( +// entry.value['ObsValue']['Value'].toString()) ?? +// 0.0, +// value1: 0.0, // Ensure value1 is passed as well +// )) +// .toList(); +// } +// +// List parseColumnChartData(List response) { +// List chartDataList = []; +// +// // Temporary map to store values by TIME_PERIOD +// Map timePeriodMap = {}; +// +// for (var entry in response) { +// String timePeriod = entry['ObsKey']['TIME_PERIOD'].toString(); +// double value = +// double.tryParse(entry['ObsValue']['Value']?.toString() ?? '0.0') ?? +// 0.0; +// String indicator = entry['ObsKey']['H_INDICATOR']; +// +// // If the entry is for TOR, map it to 'value' +// if (indicator == 'TOR') { +// if (timePeriodMap.containsKey(timePeriod)) { +// // Update the value by creating a new ChartData object +// timePeriodMap[timePeriod] = ChartData( +// timePeriod: timePeriod, +// value: value, // Set the new value +// value1: timePeriodMap[timePeriod]?.value1 ?? 0.0, +// ); +// } else { +// timePeriodMap[timePeriod] = ChartData( +// timePeriod: timePeriod, +// value: value, +// value1: 0.0, +// ); +// } +// } +// +// // If the entry is for TAR, map it to 'value1' +// if (indicator == 'TAR') { +// if (timePeriodMap.containsKey(timePeriod)) { +// // Create a new ChartData object with the updated value1 +// timePeriodMap[timePeriod] = ChartData( +// timePeriod: timePeriod, +// value: timePeriodMap[timePeriod]?.value ?? +// 0.0, // Retain the current value +// value1: value, // Update the value1 +// ); +// } else { +// timePeriodMap[timePeriod] = ChartData( +// timePeriod: timePeriod, +// value: 0.0, +// value1: value, +// ); +// } +// } +// } +// +// // Convert the map to a list +// chartDataList = timePeriodMap.values.toList(); +// +// return chartDataList; +// } +// +// Widget buildChart(dynamic chartData) { +// print('chartData $chartData'); +// switch (chartData['chart_type']) { +// case 'area': +// return SfCartesianChart( +// primaryXAxis: CategoryAxis( +// title: AxisTitle(text: 'Year'), +// labelRotation: 45, // Rotate labels if they overlap +// ), +// title: ChartTitle(text: 'Hotel Estabhlishments'), +// legend: Legend(isVisible: true), +// tooltipBehavior: TooltipBehavior( +// enable: true, +// builder: (dynamic data, dynamic point, dynamic series, +// int pointIndex, int seriesIndex) { +// return Container( +// padding: const EdgeInsets.all(8), +// decoration: BoxDecoration( +// color: Colors.white, +// borderRadius: BorderRadius.circular(5), +// ), +// child: Text( +// '${data.timePeriod}: ${formatNumber(data.value)}', +// style: const TextStyle(color: Colors.black), +// ), +// ); +// }, +// ), +// series: [ +// // AreaSeries for the area chart +// AreaSeries( +// dataSource: parseLineChartData( +// chartData['response']), // your chart data +// color: Color(0xFF7DAFBC), // Area color +// borderDrawMode: BorderDrawMode +// .excludeBottom, // Optional, adjust border settings +// borderColor: Colors.green, // Border color +// borderWidth: 2, // Border width +// xValueMapper: (ChartData data, _) => +// data.timePeriod, // Map x to DateTime +// yValueMapper: (ChartData data, _) => data.value, +// // name: chartData['dataset'], +// name: 'Hotels', +// dataLabelSettings: DataLabelSettings( +// isVisible: true, +// builder: (dynamic data, dynamic point, dynamic series, +// int pointIndex, int seriesIndex) { +// return Text( +// formatNumber(data.value), +// style: const TextStyle(fontSize: 12, color: Colors.black), +// ); +// }, +// ), // Map y to numerical value +// ) +// ]); +// case 'bar': +// return SfCartesianChart( +// primaryXAxis: CategoryAxis(), +// title: ChartTitle(text: 'Hotel Occupancy Rate'), +// tooltipBehavior: TooltipBehavior( +// enable: true, +// builder: (dynamic data, dynamic point, dynamic series, +// int pointIndex, int seriesIndex) { +// return Container( +// padding: const EdgeInsets.all(8), +// decoration: BoxDecoration( +// color: Colors.white, +// borderRadius: BorderRadius.circular(5), +// ), +// child: Text( +// '${data.timePeriod}: ${formatNumber(data.value)}', +// style: const TextStyle(color: Colors.black), +// ), +// ); +// }, +// ), +// series: >[ +// BarSeries( +// dataSource: parseBarChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => data.timePeriod, +// yValueMapper: (ChartData data, _) => data.value, +// name: chartData['dataset'], +// color: Color(0xFF7DAFBC), +// dataLabelSettings: DataLabelSettings( +// isVisible: true, +// builder: (dynamic data, dynamic point, dynamic series, +// int pointIndex, int seriesIndex) { +// return Text( +// formatNumber(data.value), +// style: const TextStyle(fontSize: 12, color: Colors.black), +// ); +// }, +// ), +// // isTrackVisible: true, +// // trackColor: Colors.red +// ), +// ], +// ); +// case 'column': +// return SfCartesianChart( +// primaryXAxis: CategoryAxis( +// title: AxisTitle(text: 'Year'), +// labelRotation: 45, // Rotate labels if they overlap +// ), +// primaryYAxis: NumericAxis( +// title: AxisTitle(text: 'No of rooms'), +// ), +// legend: Legend(isVisible: true), +// title: ChartTitle(text: 'Hotel Occupancy Rate'), +// enableSideBySideSeriesPlacement: true, +// tooltipBehavior: TooltipBehavior(enable: true), +// series: >[ +// // First Series (value) +// +// ColumnSeries( +// name: 'Available Rooms', +// // opacity: 0.9, +// // width: 0.4, +// color: Color(0xFF587BA3), +// dataSource: parseColumnChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => int.parse(data.timePeriod), +// yValueMapper: (ChartData data, _) => data.value1, +// ), +// // Second Series (value1) +// ColumnSeries( +// color: Color(0xFF90B0D5), +// name: 'Occupied Rooms', +// dataSource: parseColumnChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => int.parse(data.timePeriod), +// yValueMapper: (ChartData data, _) => data.value, +// ), +// ], // Numeric Y-axis to plot the values +// ); +// default: +// return Center(child: Text('Unknown chart type')); +// } +// } +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// backgroundColor: const Color(0xFF7DAFBC), +// appBar: AppBar( +// backgroundColor: const Color(0xFF7DAFBC), +// elevation: 0, +// leading: IconButton( +// icon: const Icon(Icons.arrow_back_ios_new, color: Colors.white), +// onPressed: () { +// context.go('/myhomepage'); +// }, +// ), +// title: const Text( +// 'Charts', +// style: TextStyle(color: Colors.white), +// ), +// ), +// body: isLoading +// ? const Center(child: CircularProgressIndicator()) +// : Column( +// children: [ +// Card( +// margin: const EdgeInsets.all(10), +// color: Colors.white, +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: const [ +// Text( +// 'Card 1 Title', +// style: TextStyle( +// fontSize: 18, fontWeight: FontWeight.bold), +// ), +// SizedBox(height: 10), +// Text('Content for the first card goes here.'), +// ], +// ), +// ), +// ), +// Card( +// margin: const EdgeInsets.all(10), +// color: Colors.white, +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: const [ +// Text( +// 'Card 2 Title', +// style: TextStyle( +// fontSize: 18, fontWeight: FontWeight.bold), +// ), +// SizedBox(height: 10), +// Text('Content for the second card goes here.'), +// ], +// ), +// ), +// ), +// Expanded( +// child: ListView.builder( +// itemCount: chartsData.length, +// itemBuilder: (context, index) { +// return Card( +// margin: const EdgeInsets.all(10), +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Container( +// height: 300, +// child: buildChart(chartsData[index]), +// ), +// ], +// ), +// ), +// ); +// }, +// ), +// ), +// ], +// ), +// ); +// } +// } +// +// class ChartData { +// final String timePeriod; +// final double value; +// final double value1; +// final String formattedValue; +// +// ChartData( +// {required this.timePeriod, required this.value, required this.value1}) +// : formattedValue = formatNumber(value); +// } +// +// // String formatNumber(double value) { +// // if (value >= 1e12) { +// // return '${(value / 1e12).toStringAsFixed(2)} T'; // Trillions +// // } else if (value >= 1e9) { +// // return '${(value / 1e9).toStringAsFixed(2)} B'; // Billions +// // } else if (value >= 1e6) { +// // return '${(value / 1e6).toStringAsFixed(2)} M'; // Millions +// // } else if (value >= 1e5) { +// // return '${(value / 1e5).toStringAsFixed(2)} L'; // Lakhs +// // } else if (value >= 1e3) { +// // return '${(value / 1e3).toStringAsFixed(2)} K'; // Thousands +// // } +// // return value.toStringAsFixed(2); // Default to two decimal places +// // } +// // String formatNumber(double value) { -// if (value >= 1e12) { -// return '${(value / 1e12).toStringAsFixed(2)} T'; // Trillions -// } else if (value >= 1e9) { +// if (value >= 1e9) { // return '${(value / 1e9).toStringAsFixed(2)} B'; // Billions // } else if (value >= 1e6) { // return '${(value / 1e6).toStringAsFixed(2)} M'; // Millions -// } else if (value >= 1e5) { -// return '${(value / 1e5).toStringAsFixed(2)} L'; // Lakhs -// } else if (value >= 1e3) { -// return '${(value / 1e3).toStringAsFixed(2)} K'; // Thousands // } -// return value.toStringAsFixed(2); // Default to two decimal places +// return value +// .toStringAsFixed(2); // Default to two decimal places for smaller numbers // } - -String formatNumber(double value) { - if (value >= 1e9) { - return '${(value / 1e9).toStringAsFixed(2)} B'; // Billions - } else if (value >= 1e6) { - return '${(value / 1e6).toStringAsFixed(2)} M'; // Millions - } - return value - .toStringAsFixed(2); // Default to two decimal places for smaller numbers -} diff --git a/lib/presentation/Screens/demo_home2.dart b/lib/presentation/Screens/demo_home2.dart index b1576aaa..2b994cdd 100644 --- a/lib/presentation/Screens/demo_home2.dart +++ b/lib/presentation/Screens/demo_home2.dart @@ -1,328 +1,328 @@ -import 'dart:convert'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:intl/intl.dart'; -import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package -import 'package:http/http.dart' as http; - -void main() { - runApp(MyApp()); -} - -class MyApp extends StatelessWidget { - @override - Widget build(BuildContext context) { - return MaterialApp( - home: ChartPage2(dataSets: ''), - ); - } -} - -class ChartPage2 extends StatefulWidget { - final String dataSets; - const ChartPage2({Key? key, required this.dataSets}); - @override - ChartPageState createState() => ChartPageState(); -} - -class ChartPageState extends State { - List chartsData = []; - bool isLoading = true; - - @override - void initState() { - super.initState(); - fetchChartData(widget.dataSets); - } - - Future fetchChartData(dataSets) async { - const baseUrl = - 'https://pb.venbait.in/api/custom/apicall'; // Replace with your server URL - final url = Uri.parse('$baseUrl?dataset=$dataSets'); - - try { - final response = await http.get(url); - if (response.statusCode == 200) { - setState(() { - chartsData = jsonDecode(response.body); - isLoading = false; - }); - } else { - throw Exception('Failed to load data'); - } - } catch (error) { - setState(() { - isLoading = false; - }); - print('Error fetching data: $error'); - } - } - - List parseLineChartData(List response) { - return response.map((entry) { - // Attempt to fetch the TIMEPERIOD - final rawTimePeriod = - entry['ObsKey']?['Year'] ?? entry['ObsKey']?['TIME_PERIOD']; - final formattedTimePeriod = rawTimePeriod != null - ? rawTimePeriod.toString() // Use as-is if valid - : 'Unknown'; // Fallback value if null - - // Parse the value or fallback to 0.0 - final value = - double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? - 0.0; - - return ChartData( - timePeriod: formattedTimePeriod, - value: value, - ); - }).toList(); - } - - List parseBarChartData(List response) { - return response - .asMap() - .entries - .map((entry) => ChartData( - timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ?? - 'Unknown', // Fallback to 'Unknown' if null - value: double.tryParse( - entry.value['ObsValue']['Value'].toString()) ?? - 0.0, // Handle null/invalid value - )) - .toList(); - } - -// Update parseColumnChartData for column chart - List parseColumnChartData(List response) { - return response.map((entry) { - // Extract TIME_PERIOD as x - final x = - int.tryParse(entry['ObsKey']?['TIME_PERIOD']?.toString() ?? '0') ?? 0; - // Extract Value as y - final y = - double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? - 0.0; - return ChartData(xInt: x, y: y); - }).toList(); - } - -// Update parseScatterChartData for scatter chart - List parseScatterChartData(List response) { - return response.map((entry) { - // Parse TIME_PERIOD into DateTime - final timePeriod = entry['ObsKey']?['TIME_PERIOD']; - final x = timePeriod != null - ? DateTime.tryParse('$timePeriod-01-01') // Convert year to DateTime - : DateTime.now(); // Fallback to current date if null - - // Parse Value into double - final y = - double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? - 0.0; - - return ChartData(xDateTime: x, y: y); - }).toList(); - } - - Widget buildChart(dynamic chartData) { - switch (chartData['chart_type']) { - case 'scatter': - return SfCartesianChart( - primaryXAxis: DateTimeAxis(), // Ensure it's DateTimeAxis - tooltipBehavior: TooltipBehavior(enable: true), - title: ChartTitle(text: 'Divorces'), - series: [ - ScatterSeries( - dataSource: parseScatterChartData(chartData['response']), - xValueMapper: (ChartData data, _) => - data.xDateTime ?? DateTime.now(), - yValueMapper: (ChartData data, _) => data.y, - name: chartData['dataset'], - dataLabelSettings: DataLabelSettings(isVisible: true), - ), - ], - ); - case 'column': - return SfCartesianChart( - primaryXAxis: NumericAxis( - title: AxisTitle(text: 'Year'), - interval: 1, // Ensure no fractional intervals - ), - title: ChartTitle(text: 'Marriages'), - series: >[ - ColumnSeries( - dataSource: parseColumnChartData(chartData['response']), - xValueMapper: (ChartData data, _) => data.xInt ?? 0, - yValueMapper: (ChartData data, _) => data.y, - name: chartData['dataset'], - color: Color(0xFF7DAFBC), - width: 0.8, - spacing: 0.2, - dataLabelSettings: - DataLabelSettings(isVisible: true), // Show data labels - ), - ], - ); - - case 'line': - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: 'Line Chart'), - legend: Legend(isVisible: true), - tooltipBehavior: TooltipBehavior(enable: true), - series: >[ - LineSeries( - dataSource: parseLineChartData(chartData['response']), - xValueMapper: (ChartData data, _) => data.timePeriod ?? '', - yValueMapper: (ChartData data, _) => data.value ?? 0.0, - name: 'Sales', - dataLabelSettings: DataLabelSettings(isVisible: true), - ), - ], - ); - case 'bar': - return SfCartesianChart( - primaryXAxis: CategoryAxis(), - title: ChartTitle(text: 'Bar Chart'), - tooltipBehavior: TooltipBehavior(enable: true), - series: >[ - BarSeries( - dataSource: parseBarChartData(chartData['response']), - xValueMapper: (ChartData data, _) => data.timePeriod ?? '', - yValueMapper: (ChartData data, _) => data.value ?? 0.0, - name: 'Gold', - color: Color.fromRGBO(8, 142, 255, 1), - ), - ], - ); - - default: - return Center(child: Text('Unknown chart type')); - } - } - - @override - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: const Color(0xFF7DAFBC), - appBar: AppBar( - backgroundColor: Color(0xFF7DAFBC), - elevation: 0, - leading: IconButton( - icon: Icon(Icons.arrow_back_ios_new, color: Colors.white), - onPressed: () { - context.go('/myhomepage'); - }, - ), - title: Text( - 'Charts', - style: TextStyle(color: Colors.white), - ), - ), - body: isLoading - ? Center(child: CircularProgressIndicator()) - : Column( - children: [ - // Row for two cards - Row( - children: [ - // First Card - Expanded( - flex: 6, - child: Card( - margin: EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Card 1', - style: TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), - ), - SizedBox(height: 10), - Text('Content for Card 1 goes here.'), - ], - ), - ), - ), - ), - // Second Card - Expanded( - flex: 6, - child: Card( - margin: EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Card 2', - style: TextStyle( - fontSize: 18, fontWeight: FontWeight.bold), - ), - SizedBox(height: 10), - Text('Content for Card 2 goes here.'), - ], - ), - ), - ), - ), - ], - ), - // List of charts - Expanded( - child: ListView.builder( - itemCount: chartsData.length, - itemBuilder: (context, index) { - return Card( - margin: EdgeInsets.all(10), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: 10), - Container( - height: 300, - child: buildChart(chartsData[index]), - ), - ], - ), - ), - ); - }, - ), - ), - ], - ), - ); - } -} - -class ChartData { - final int? xInt; // For column and other charts, using int for x - final DateTime? xDateTime; // For scatter charts, using DateTime for x - final double y; // For column and other charts - final String? timePeriod; // For line and bar charts - final double? value; // For line and bar charts - final double? y2; // For secondary data (stacked column chart) - final double? y3; // For tertiary data (stacked column chart) - final double? y4; // For quaternary data (stacked column chart) - - // Constructor that handles all chart cases - ChartData({ - this.xInt, - this.xDateTime, - this.y = 0.0, - this.timePeriod, - this.value, - this.y2, - this.y3, - this.y4, - }); -} +// import 'dart:convert'; +// import 'package:flutter/material.dart'; +// import 'package:go_router/go_router.dart'; +// import 'package:intl/intl.dart'; +// import 'package:syncfusion_flutter_charts/charts.dart'; // Import Syncfusion charts package +// import 'package:http/http.dart' as http; +// +// void main() { +// runApp(MyApp()); +// } +// +// class MyApp extends StatelessWidget { +// @override +// Widget build(BuildContext context) { +// return MaterialApp( +// home: ChartPage2(dataSets: ''), +// ); +// } +// } +// +// class ChartPage2 extends StatefulWidget { +// final String dataSets; +// const ChartPage2({Key? key, required this.dataSets}); +// @override +// ChartPageState createState() => ChartPageState(); +// } +// +// class ChartPageState extends State { +// List chartsData = []; +// bool isLoading = true; +// +// @override +// void initState() { +// super.initState(); +// fetchChartData(widget.dataSets); +// } +// +// Future fetchChartData(dataSets) async { +// const baseUrl = +// 'https://pb.venbait.in/api/custom/apicall'; // Replace with your server URL +// final url = Uri.parse('$baseUrl?dataset=$dataSets'); +// +// try { +// final response = await http.get(url); +// if (response.statusCode == 200) { +// setState(() { +// chartsData = jsonDecode(response.body); +// isLoading = false; +// }); +// } else { +// throw Exception('Failed to load data'); +// } +// } catch (error) { +// setState(() { +// isLoading = false; +// }); +// print('Error fetching data: $error'); +// } +// } +// +// List parseLineChartData(List response) { +// return response.map((entry) { +// // Attempt to fetch the TIMEPERIOD +// final rawTimePeriod = +// entry['ObsKey']?['Year'] ?? entry['ObsKey']?['TIME_PERIOD']; +// final formattedTimePeriod = rawTimePeriod != null +// ? rawTimePeriod.toString() // Use as-is if valid +// : 'Unknown'; // Fallback value if null +// +// // Parse the value or fallback to 0.0 +// final value = +// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? +// 0.0; +// +// return ChartData( +// timePeriod: formattedTimePeriod, +// value: value, +// ); +// }).toList(); +// } +// +// List parseBarChartData(List response) { +// return response +// .asMap() +// .entries +// .map((entry) => ChartData( +// timePeriod: entry.value['ObsKey']['TIME_PERIOD'] ?? +// 'Unknown', // Fallback to 'Unknown' if null +// value: double.tryParse( +// entry.value['ObsValue']['Value'].toString()) ?? +// 0.0, // Handle null/invalid value +// )) +// .toList(); +// } +// +// // Update parseColumnChartData for column chart +// List parseColumnChartData(List response) { +// return response.map((entry) { +// // Extract TIME_PERIOD as x +// final x = +// int.tryParse(entry['ObsKey']?['TIME_PERIOD']?.toString() ?? '0') ?? 0; +// // Extract Value as y +// final y = +// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? +// 0.0; +// return ChartData(xInt: x, y: y); +// }).toList(); +// } +// +// // Update parseScatterChartData for scatter chart +// List parseScatterChartData(List response) { +// return response.map((entry) { +// // Parse TIME_PERIOD into DateTime +// final timePeriod = entry['ObsKey']?['TIME_PERIOD']; +// final x = timePeriod != null +// ? DateTime.tryParse('$timePeriod-01-01') // Convert year to DateTime +// : DateTime.now(); // Fallback to current date if null +// +// // Parse Value into double +// final y = +// double.tryParse(entry['ObsValue']?['Value']?.toString() ?? '0.0') ?? +// 0.0; +// +// return ChartData(xDateTime: x, y: y); +// }).toList(); +// } +// +// Widget buildChart(dynamic chartData) { +// switch (chartData['chart_type']) { +// case 'scatter': +// return SfCartesianChart( +// primaryXAxis: DateTimeAxis(), // Ensure it's DateTimeAxis +// tooltipBehavior: TooltipBehavior(enable: true), +// title: ChartTitle(text: 'Divorces'), +// series: [ +// ScatterSeries( +// dataSource: parseScatterChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => +// data.xDateTime ?? DateTime.now(), +// yValueMapper: (ChartData data, _) => data.y, +// name: chartData['dataset'], +// dataLabelSettings: DataLabelSettings(isVisible: true), +// ), +// ], +// ); +// case 'column': +// return SfCartesianChart( +// primaryXAxis: NumericAxis( +// title: AxisTitle(text: 'Year'), +// interval: 1, // Ensure no fractional intervals +// ), +// title: ChartTitle(text: 'Marriages'), +// series: >[ +// ColumnSeries( +// dataSource: parseColumnChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => data.xInt ?? 0, +// yValueMapper: (ChartData data, _) => data.y, +// name: chartData['dataset'], +// color: Color(0xFF7DAFBC), +// width: 0.8, +// spacing: 0.2, +// dataLabelSettings: +// DataLabelSettings(isVisible: true), // Show data labels +// ), +// ], +// ); +// +// case 'line': +// return SfCartesianChart( +// primaryXAxis: CategoryAxis(), +// title: ChartTitle(text: 'Line Chart'), +// legend: Legend(isVisible: true), +// tooltipBehavior: TooltipBehavior(enable: true), +// series: >[ +// LineSeries( +// dataSource: parseLineChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => data.timePeriod ?? '', +// yValueMapper: (ChartData data, _) => data.value ?? 0.0, +// name: 'Sales', +// dataLabelSettings: DataLabelSettings(isVisible: true), +// ), +// ], +// ); +// case 'bar': +// return SfCartesianChart( +// primaryXAxis: CategoryAxis(), +// title: ChartTitle(text: 'Bar Chart'), +// tooltipBehavior: TooltipBehavior(enable: true), +// series: >[ +// BarSeries( +// dataSource: parseBarChartData(chartData['response']), +// xValueMapper: (ChartData data, _) => data.timePeriod ?? '', +// yValueMapper: (ChartData data, _) => data.value ?? 0.0, +// name: 'Gold', +// color: Color.fromRGBO(8, 142, 255, 1), +// ), +// ], +// ); +// +// default: +// return Center(child: Text('Unknown chart type')); +// } +// } +// +// @override +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// backgroundColor: const Color(0xFF7DAFBC), +// appBar: AppBar( +// backgroundColor: Color(0xFF7DAFBC), +// elevation: 0, +// leading: IconButton( +// icon: Icon(Icons.arrow_back_ios_new, color: Colors.white), +// onPressed: () { +// context.go('/myhomepage'); +// }, +// ), +// title: Text( +// 'Charts', +// style: TextStyle(color: Colors.white), +// ), +// ), +// body: isLoading +// ? Center(child: CircularProgressIndicator()) +// : Column( +// children: [ +// // Row for two cards +// Row( +// children: [ +// // First Card +// Expanded( +// flex: 6, +// child: Card( +// margin: EdgeInsets.all(10), +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// 'Card 1', +// style: TextStyle( +// fontSize: 18, fontWeight: FontWeight.bold), +// ), +// SizedBox(height: 10), +// Text('Content for Card 1 goes here.'), +// ], +// ), +// ), +// ), +// ), +// // Second Card +// Expanded( +// flex: 6, +// child: Card( +// margin: EdgeInsets.all(10), +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// Text( +// 'Card 2', +// style: TextStyle( +// fontSize: 18, fontWeight: FontWeight.bold), +// ), +// SizedBox(height: 10), +// Text('Content for Card 2 goes here.'), +// ], +// ), +// ), +// ), +// ), +// ], +// ), +// // List of charts +// Expanded( +// child: ListView.builder( +// itemCount: chartsData.length, +// itemBuilder: (context, index) { +// return Card( +// margin: EdgeInsets.all(10), +// child: Padding( +// padding: const EdgeInsets.all(16.0), +// child: Column( +// crossAxisAlignment: CrossAxisAlignment.start, +// children: [ +// SizedBox(height: 10), +// Container( +// height: 300, +// child: buildChart(chartsData[index]), +// ), +// ], +// ), +// ), +// ); +// }, +// ), +// ), +// ], +// ), +// ); +// } +// } +// +// class ChartData { +// final int? xInt; // For column and other charts, using int for x +// final DateTime? xDateTime; // For scatter charts, using DateTime for x +// final double y; // For column and other charts +// final String? timePeriod; // For line and bar charts +// final double? value; // For line and bar charts +// final double? y2; // For secondary data (stacked column chart) +// final double? y3; // For tertiary data (stacked column chart) +// final double? y4; // For quaternary data (stacked column chart) +// +// // Constructor that handles all chart cases +// ChartData({ +// this.xInt, +// this.xDateTime, +// this.y = 0.0, +// this.timePeriod, +// this.value, +// this.y2, +// this.y3, +// this.y4, +// }); +// } diff --git a/lib/presentation/Screens/profilepage.dart b/lib/presentation/Screens/profilepage.dart index e003a18f..d9c8ec7b 100644 --- a/lib/presentation/Screens/profilepage.dart +++ b/lib/presentation/Screens/profilepage.dart @@ -37,6 +37,8 @@ class _ProfileScreenState extends State { true ]; // One for each TextField + bool _isLoading = false; + String _getControllerText(int index) { if (index == 0) return _usernameController.text; if (index == 1) return _emailController.text; @@ -169,6 +171,9 @@ class _ProfileScreenState extends State { // Function to open the date picker void _pickImage() async { + setState(() { + _isLoading = true; // Start loading + }); final XFile? pickedFile = await _picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { @@ -179,6 +184,7 @@ class _ProfileScreenState extends State { fileExtension == 'png' || fileExtension == "heic") { setState(() { + _isLoading = false; _profileImage = File(pickedFile.path); }); } else { @@ -365,28 +371,68 @@ class _ProfileScreenState extends State { padding: const EdgeInsets.all(20.0), child: Column( children: [ - CircleAvatar( - radius: 50, - backgroundImage: _profileImage != null - ? FileImage(_profileImage!) - : AssetImage("assets/edit_profile/profile.png") - as ImageProvider, - child: Align( - alignment: Alignment.bottomRight, - child: GestureDetector( - onTap: _pickImage, // Call `_pickImage` on tap - child: CircleAvatar( - radius: 15, - backgroundColor: Colors.white, - child: Icon( - Icons.camera_alt, - size: 15, - color: Colors.grey, + // CircleAvatar( + // radius: 50, + // backgroundImage: _profileImage != null + // ? FileImage(_profileImage!) + // : AssetImage("assets/edit_profile/profile.png") + // as ImageProvider, + // child: Align( + // alignment: Alignment.bottomRight, + // child: GestureDetector( + // onTap: _pickImage, // Call `_pickImage` on tap + // child: CircleAvatar( + // radius: 15, + // backgroundColor: Colors.white, + // child: Icon( + // Icons.camera_alt, + // size: 15, + // color: Colors.grey, + // ), + // ), + // ), + // ), + // ), + + Stack( + alignment: Alignment.center, + children: [ + // CircleAvatar with the profile image + CircleAvatar( + radius: 50, + backgroundImage: _profileImage != null + ? FileImage(_profileImage!) + : AssetImage("assets/edit_profile/profile.png") + as ImageProvider, + child: Align( + alignment: Alignment.bottomRight, + child: GestureDetector( + onTap: _pickImage, // Call `_pickImage` on tap + child: CircleAvatar( + radius: 15, + backgroundColor: Colors.white, + child: Icon( + Icons.camera_alt, + size: 15, + color: Colors.grey, + ), + ), ), ), ), - ), + + // Conditional loader that shows when _isLoading is true + if (_isLoading) + Positioned( + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: + AlwaysStoppedAnimation(Colors.grey), + ), + ), + ], ), + SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.start, 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 9779c61f..035f27c6 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 @@ -160,9 +160,13 @@ // ); // } // } +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:go_router/go_router.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 { const MyHomePage({super.key}); @@ -171,18 +175,28 @@ class MyHomePage extends StatefulWidget { State createState() => _MyHomePageState(); } -void handleInfoCardClick(BuildContext context, String data) { +void handleInfoCardClick(BuildContext context, String data, Color color) { // Handle navigation and pass dynamic data print(data); final dataSets = data; - print(dataSets); - if (dataSets == 'hotels') { - context.go('/chartScreen/$dataSets'); - } else if (dataSets == 'divorces') { - context.go('/chartScreen/$dataSets'); - } else if (dataSets == 'marriages') { + 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 { @@ -191,243 +205,242 @@ class _MyHomePageState extends State { double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; return BaseScaffold( - mycenterTitle: true, - title: SizedBox( - height: myheight / 5, - width: mywidth / 3, - child: Image(image: AssetImage('assets/logos/uae_stat.png'))), - body: EconomyStats(), + title: Center( + child: SizedBox( + height: myheight / 5, + width: mywidth / 3, + child: Image(image: AssetImage('assets/logos/uae_stat.png')))), + body: EconomyStatsWidget(), ); } } -class EconomyStats extends StatelessWidget { - const EconomyStats({Key? key}) : super(key: key); +class EconomyStatsWidget extends StatefulWidget { + const EconomyStatsWidget({super.key}); @override + EconomyStatsState createState() => EconomyStatsState(); +} + +class EconomyStatsState extends State { + List data = []; + + bool isLoading = true; + + void initState() { + fetchData(); + } + + Future fetchData() async { + const baseUrl = 'https://pb.venbait.in/api/getHomePageData'; + try { + final response = await http.get(Uri.parse(baseUrl)); + if (response.statusCode == 200) { + setState(() { + data = json.decode(response.body); + print("HomeDAta $data"); + isLoading = false; + }); + } else { + throw Exception('Failed to load data'); + } + } catch (e) { + setState(() { + isLoading = false; + }); + print('Error fetching data: $e'); + } + } + Widget build(BuildContext context) { - return SingleChildScrollView( - child: Padding( - padding: EdgeInsets.only(left: 10, right: 10), - child: Column( - children: [ - // Header Section - RoundedCornerContainer( - text: 'ECONOMY', - backgroundColor: Color(0xFF7DAFBC), - textStyle: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - // Grid Section + double myheight = MediaQuery.of(context).size.height; + double mywidth = MediaQuery.of(context).size.width; - // ECONOMY DETAILS - Padding( - padding: const EdgeInsets.only( - top: 10, - ), - child: Column( - children: [ - // First Row - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + return Expanded( + child: Container( + height: myheight, + // color: Colors.cyan, + child: Padding( + padding: EdgeInsets.only(left: 10, right: 10), + child: Column( + children: data.map((mainTopic) { + String colorPattern = mainTopic['color_pattern']; + Color backgroundColor = Color(int.parse(colorPattern)); + Color borderColor = + backgroundColor; // Use the same color for the border + + // final totalTiles = mainTopic['sub_topics'] + // .expand((subTopic) => (subTopic['tile_data'] as List? ?? [])) + // .length; + + final totalTiles = + (mainTopic['tile_data'] as List?)?.length ?? 0; + + // Compute crossAxisCount dynamically + final crossAxisCount = + (totalTiles / 2).ceil(); // Divide by 2 and round up + + return Expanded( + child: Container( + // color: Colors.cyan, + height: + myheight, // Set the background color for the entire Column + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - InfoCard( - title: 'GDP (Constant)', - subtitle: '(2022) (AED)', - value: '1.62T', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick(context, 'Inflation'), + RoundedCornerContainer( + text: mainTopic['main_topic'], + backgroundColor: backgroundColor, + textStyle: const TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500, + color: Colors.white, + ), ), - InfoCard( - title: 'Inflation Rate', - subtitle: '(2022)', - value: '4.82%', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick(context, 'Inflation'), + SizedBox( + height: myheight / 4.8, // Set dynamic height + child: Container( + // color: Colors.grey[200], // Set a background color for the scrollable container + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: MediaQuery.of(context).size.width * 2, + ), + child: MasonryGridView.builder( + gridDelegate: + SliverSimpleGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, + ), + physics: const NeverScrollableScrollPhysics(), + itemCount: totalTiles, + itemBuilder: (context, index) { + final tileDataList = (mainTopic['tile_data'] + as List?) ?? + []; + final tileData = Map.from( + tileDataList[index]); + + return Container( + margin: const EdgeInsets.all(0), + height: myheight / + 9.2, // Variable height for tiles + width: mywidth / 3, + padding: + const EdgeInsets.only(bottom: 3, top: 3), + // color: Colors.yellowAccent.shade100, // Tile background color + child: Flexible( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: _buildRows( + [tileData], + borderColor, + mainTopic['color_pattern']), + ), + ), + ); + }, + ), + ), + ), ), ], ), - const SizedBox(height: 10), - // Second Row - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Trade Value', - subtitle: '(Jan - Jan 2024) - AED', - value: '215.4B', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => - handleInfoCardClick(context, 'commodities'), - ), - InfoCard( - title: 'Hotel Guests', - subtitle: '(2022)', - value: '25.21M', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick(context, 'hotels'), - ), - ], - ), - const SizedBox(height: 10), - RoundedCornerContainer( - text: 'SOCIAL', - backgroundColor: Colors.brown[200]!, - textStyle: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - - // SOCIAL DETAILS - Padding( - padding: const EdgeInsets.only( - top: 10, - ), - child: Column( - children: [ - // First Row - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Divorces', - subtitle: '(2022)', - value: '0', - bordercolor: Colors.brown[200]!, - textcolor: Colors.brown[200]!, - onTap: () => - handleInfoCardClick(context, 'divorces'), - ), - InfoCard( - title: 'Marriages', - subtitle: '(2022)', - value: '0', - bordercolor: Colors.brown[200]!, - textcolor: Colors.brown[200]!, - onTap: () => - handleInfoCardClick(context, 'marriages'), - ), - ], - ), - const SizedBox(height: 10), - // Second Row - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Hospitals (Gov)', - subtitle: '(Jan - Jan 2024) - AED', - value: '215.4B', - bordercolor: Colors.brown[200]!, - textcolor: Colors.brown[200]!, - onTap: () => - handleInfoCardClick(context, 'Hospitals'), - ), - InfoCard( - title: 'Schools', - subtitle: '(2022)', - value: '25.21M', - bordercolor: Colors.brown[200]!, - textcolor: Colors.brown[200]!, - onTap: () => - handleInfoCardClick(context, 'Schools'), - ), - ], - ), - const SizedBox(height: 10), - RoundedCornerContainer( - text: 'ENVIRONMENT', - backgroundColor: Color(0xFF7DAFBC), - textStyle: const TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - - // ENVIRONMENT DETAILS - Padding( - padding: const EdgeInsets.only( - top: 10, - ), - child: Column( - children: [ - // First Row - Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Electricity', - subtitle: '(2022) (AED)', - value: '1.62T', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick( - context, 'Electricity'), - ), - InfoCard( - title: 'Cruid oil', - subtitle: '(2022)', - value: '4.82%', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => - handleInfoCardClick(context, 'Cruid'), - ), - ], - ), - const SizedBox(height: 10), - // Second Row - Row( - mainAxisAlignment: - MainAxisAlignment.spaceEvenly, - children: [ - InfoCard( - title: 'Export of oil', - subtitle: '(Jan - Jan 2024) - AED', - value: '215.4B', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => - handleInfoCardClick(context, 'Export'), - ), - InfoCard( - title: 'Desalinated water', - subtitle: '(2022)', - value: '25.21M', - bordercolor: Color(0xFF7DAFBC), - textcolor: Color(0xFF7DAFBC), - onTap: () => handleInfoCardClick( - context, 'Desalinated'), - ), - ], - ), - ], - ), - ), - ], - ), - ), - ], - ), - ), - ], + ), + ); + }).toList(), + ), ), ), ); } } +// +// List _buildRows(List> tileData, Color borderColor) { +// +// +// List rows = []; +// +// +// for (int i = 0; i < tileData.length; i += 2) { +// // Adding a row for every two items +// rows.add( +// Container( +// // color: Colors.grey[200], // Set background color for the row +// // padding: EdgeInsets.symmetric(vertical: 7.0), // Optional padding +// padding: EdgeInsets.only(top:5, bottom:2), +// child: Expanded( +// child: Row( +// mainAxisAlignment: MainAxisAlignment.start, +// children: tileData +// .sublist(i, i + 2 > tileData.length ? tileData.length : i + 2) +// .map((tile) { +// return Expanded( +// child: InfoCard( +// title: tile['data_set_tile_heading']!, +// subtitle: tile['value_source']!, +// value: tile['value']!, +// dataset:tile['data_set']!, +// bordercolor: borderColor, +// textcolor: borderColor, +// backgroundColor: Colors.white, // Set background color for InfoCard +// onTap: () => {}, +// +// ), +// ); +// }).toList(), +// ), +// ), +// ), +// ); +// // rows.add(SizedBox(height: 1)); // Add space between rows +// } +// +// return rows; +// } + +List _buildRows( + List> tileData, Color borderColor, color_pattern) { + // Sort the tileData list based on 'data_set_list_order' + // tileData.sort((a, b) => a['data_set_list_order'].compareTo(b['data_set_list_order'])); + tileData.sort((a, b) => + (a['data_set_list_order'] ?? 0).compareTo(b['data_set_list_order'] ?? 0)); + + List rows = []; + print('tileDataLength: $tileData.length'); + + for (int i = 0; i < tileData.length; i += 2) { + // Adding a row for every two items + rows.add( + Container( + padding: EdgeInsets.only(top: 5, bottom: 2), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: tileData + .sublist(i, i + 2 > tileData.length ? tileData.length : i + 2) + .map((tile) { + return Expanded( + child: InfoCard( + title: tile['data_set_tile_heading']!, + subtitle: tile['value_source']!, + value: tile['value']!, + dataset: tile['data_set']!, + bordercolor: borderColor, + textcolor: borderColor, + backgroundColor: + Colors.white, // Set background color for InfoCard + onTap: () {}, + ), + ); + }).toList(), + ), + ), + ); + } + + return rows; +} + class RoundedCornerContainer extends StatelessWidget { final String text; final Color backgroundColor; @@ -445,7 +458,7 @@ class RoundedCornerContainer extends StatelessWidget { return Container( decoration: BoxDecoration( color: backgroundColor, - borderRadius: BorderRadius.circular(12), // Rounded corners + borderRadius: BorderRadius.circular(16), // Rounded corners ), child: Center( child: Text( @@ -466,18 +479,22 @@ class InfoCard extends StatelessWidget { final String title; final String subtitle; final String value; + final String dataset; final Color bordercolor; final Color? textcolor; - final VoidCallback onTap; // New callback parameter + final VoidCallback onTap; + final Color backgroundColor; const InfoCard({ Key? key, required this.title, required this.subtitle, required this.value, + required this.dataset, required this.bordercolor, this.textcolor, - required this.onTap, // Initialize in constructor + required this.onTap, + required this.backgroundColor, }) : super(key: key); @override @@ -485,50 +502,84 @@ class InfoCard extends StatelessWidget { double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; - return Expanded( - child: GestureDetector( - onTap: onTap, // Use callback for handling clicks - child: Container( - height: myheight / 9, - width: mywidth / 4, - margin: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - border: Border.all(color: bordercolor), - borderRadius: BorderRadius.circular(8), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Text( - title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - color: Colors.black, + return GestureDetector( + onTap: () { + // Call handleInfoCardClick and pass the title and color + handleInfoCardClick( + context, dataset, bordercolor); // Pass 'title' to the function + }, + child: Container( + height: myheight / 12, + + width: mywidth / 4, + + margin: const EdgeInsets.symmetric(horizontal: 8), + // padding: const EdgeInsets.all(10), + padding: const EdgeInsets.only(bottom: 1, top: 1, left: 10, right: 10), + decoration: BoxDecoration( + border: Border.all(color: bordercolor), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Flexible( + fit: FlexFit.loose, + child: FittedBox( + fit: BoxFit.contain, // Adjust text to fit within the bounds + child: Padding( + // padding: const EdgeInsets.all(4.0), + padding: const EdgeInsets.only(top: 4, bottom: 0), + child: Text( + title, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.black, + ), ), ), ), - const SizedBox(height: 2), - Text( - subtitle, - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 12, - color: Colors.grey, + ), + const SizedBox(height: 0.5), + + Flexible( + fit: FlexFit.loose, + child: FittedBox( + fit: BoxFit.contain, + child: Column( + children: [ + Text( + subtitle, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 8, + color: Colors.grey, + ), + ), + ], ), ), - const SizedBox(height: 2), - Text( - value, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - color: textcolor ?? Colors.black, - ), + ), + const SizedBox(height: 1), + + // Flexible( + // fit: FlexFit.loose, + // child: FittedBox( + // fit: BoxFit.scaleDown, + // child: + Text( + value, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: textcolor ?? Colors.black, ), - ], - ), + ), + // ), + // ), + ], ), ), ); diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart index 8c513234..f87edba9 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart @@ -23,6 +23,7 @@ class _EditProfileState extends State { final _pb = PocketBase('https://pb.venbait.in'); // final _pb = PocketBase('http://127.0.0.1:8090'); bool _isProfileCompleted = false; + bool _isLoading = false; // Add focus nodes and hint states final List _focusNodes = List.generate(4, (_) => FocusNode()); @@ -207,16 +208,30 @@ class _EditProfileState extends State { // Function to open the date picker void _pickImage() async { + // setState(() { + // _isLoading = true; // Show loader + // print("Im Loading"); + // }); + // + // // Simulate an image loading process or network request + // await Future.delayed(Duration(seconds: 8)); // Example delay + final XFile? pickedFile = await _picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { + setState(() { + _isLoading = true; // Start loading + }); + print(pickedFile); final String fileExtension = pickedFile.path.split('.').last.toLowerCase(); + print('fileExtension $fileExtension'); if (fileExtension == 'jpg' || fileExtension == 'jpeg' || fileExtension == 'png' || fileExtension == "heic") { setState(() { + _isLoading = false; _profileImage = File(pickedFile.path); }); } else { @@ -225,6 +240,10 @@ class _EditProfileState extends State { ); } } + + setState(() { + _isLoading = false; // Hide loader + }); } Future _pickDate() async { @@ -369,6 +388,17 @@ class _EditProfileState extends State { }); } + Future _loadImage(String url) async { + try { + // Simulate a network delay of 2 seconds (this mimics loading an image from the internet) + // await Future.delayed(Duration(seconds: 3)); + print("Image loaded from URL: $url"); + } catch (e) { + print("Error loading image: $e"); + throw Exception('Failed to load image'); + } + } + @override Widget build(BuildContext context) { return BaseScaffold( @@ -385,34 +415,112 @@ class _EditProfileState extends State { padding: const EdgeInsets.all(20.0), child: Column( children: [ - CircleAvatar( - radius: 50, - backgroundImage: _profileImage != null - ? FileImage( - _profileImage!) // If a local file is selected - : _avatarUrl.isNotEmpty - ? NetworkImage(_avatarUrl) // Load from URL - : AssetImage( - "assets/edit_profile/profile.png") - as ImageProvider, + // CircleAvatar( + // radius: 50, + // backgroundImage: _profileImage != null + // ? FileImage( + // _profileImage!) // If a local file is selected + // : _avatarUrl.isNotEmpty + // ? NetworkImage(_avatarUrl) // Load from URL + // : AssetImage( + // "assets/edit_profile/profile.png") + // as ImageProvider, + // + // //backgroundImage: NetworkImage(_avatarUrl) as ImageProvider, + // + // child: + // Align( + // alignment: Alignment.bottomRight, + // child: GestureDetector( + // onTap: _pickImage, // Call `_pickImage` on tap + // child: CircleAvatar( + // radius: 15, + // backgroundColor: Colors.white, + // child: Icon( + // Icons.camera_alt, + // size: 15, + // color: Colors.grey, + // ), + // ), + // ), + // ), + // ), - //backgroundImage: NetworkImage(_avatarUrl) as ImageProvider, - child: Align( - alignment: Alignment.bottomRight, - child: GestureDetector( - onTap: _pickImage, // Call `_pickImage` on tap - child: CircleAvatar( - radius: 15, - backgroundColor: Colors.white, - child: Icon( - Icons.camera_alt, - size: 15, - color: Colors.grey, + Stack( + alignment: Alignment.center, + children: [ + CircleAvatar( + radius: 50, + backgroundImage: _profileImage != null + ? FileImage( + _profileImage!) // If a local file is selected + : _avatarUrl.isNotEmpty + ? NetworkImage( + _avatarUrl) // Load from URL + : AssetImage( + "assets/edit_profile/profile.png") + as ImageProvider, + child: _avatarUrl.isNotEmpty + ? FutureBuilder( + future: _loadImage(_avatarUrl), + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.waiting) { + return Center( + child: CircularProgressIndicator(), + ); + } else if (snapshot.hasError) { + return Center( + child: Icon(Icons.error), + ); + } else { + return Align( + alignment: Alignment.bottomRight, + child: GestureDetector( + onTap: + _pickImage, // Call `_pickImage` on tap + child: CircleAvatar( + radius: 15, + backgroundColor: Colors.white, + child: Icon( + Icons.camera_alt, + size: 15, + color: Colors.grey, + ), + ), + ), + ); + } + }, + ) + : Align( + alignment: Alignment.bottomRight, + child: GestureDetector( + onTap: + _pickImage, // Call `_pickImage` on tap + child: CircleAvatar( + radius: 15, + backgroundColor: Colors.white, + child: Icon( + Icons.camera_alt, + size: 15, + color: Colors.grey, + ), + ), + ), + ), + ), + if (_isLoading) + Positioned( + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + Colors.grey), ), ), - ), - ), + ], ), + SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.start, diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart index 7934ed17..6be42872 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart @@ -527,7 +527,7 @@ class _FeedbackFormState extends State )), RatingBar.builder( initialRating: rating, - minRating: 1, + minRating: 0, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart index 8652db61..3f3cfc5a 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/manage_users.dart @@ -54,7 +54,7 @@ class _ManageUserRouterState extends State { final formattedDate = DateFormat('dd/MM/yyyy').format(createdDate); return User( id: record.id, // Correctly passing the ID - userName: record.getStringValue('username'), + userName: record.getStringValue('uname'), emailId: record.getStringValue('email'), registrationDate: formattedDate, status: record.getStringValue('status'), diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index a9474e57..5bdb2c9f 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:pocketbase/pocketbase.dart'; +import 'package:shared_preferences/shared_preferences.dart'; -class BaseScaffold extends StatelessWidget { +class BaseScaffold extends StatefulWidget { final Widget body; final Widget title; - final bool ? showBackButton; - final bool ? mycenterTitle; + final bool? showBackButton; + final bool? mycenterTitle; final List? actions; final Color? appbarColor, mytitleColor; @@ -15,76 +17,126 @@ class BaseScaffold extends StatelessWidget { required this.title, this.actions, this.showBackButton = false, - this.mycenterTitle = false, this.appbarColor, this.mytitleColor + this.mycenterTitle = false, + this.appbarColor, + this.mytitleColor, }) : super(key: key); - // final Widget body; - // - // const BaseScaffold({required this.body}); + + @override + _BaseScaffoldState createState() => _BaseScaffoldState(); +} + +class _BaseScaffoldState extends State { + final _pb = PocketBase('https://pb.venbait.in'); + String _avatarUrl = ''; + dynamic userId; + String? userName; + String? userEmail; + String? userAvatar; + String? role; + + @override + void initState() { + super.initState(); + _checkUserId(); + } + + // Method to retrieve userId from SharedPreferences + Future getUserId() async { + final prefs = await SharedPreferences.getInstance(); + return prefs.getString('userId'); // Retrieve the userId + } + + // Method to check if userId exists and update state + Future _checkUserId() async { + String? fetchedUserId = await getUserId(); + if (fetchedUserId != null && fetchedUserId.isNotEmpty) { + setState(() { + userId = fetchedUserId; + }); + //print('NAVUser ID: $userId'); + _fetchUserData(); + } else { + print('No userId found'); + // Handle the case where userId is not available + } + } + + Future _fetchUserData() async { + try { + final adminAuth = await _pb.admins + .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); + final adminToken = adminAuth.token; + //print('adminToken- ${adminToken}'); + final userDetailsResponse = await _pb.collection('users').getOne( + userId!, + headers: { + 'Authorization': 'Bearer $adminToken', + }, + ); + print('NAVuserDetails: $userDetailsResponse'); + + setState(() { + userName = userDetailsResponse.data['uname'] ?? ''; + userEmail = userDetailsResponse.data['email'] ?? ''; + userAvatar = userDetailsResponse.data['avatar'] ?? ''; + role = userDetailsResponse.data['role'] ?? ''; + String recordId = userId; + String collectionId = + userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_'; + + if (userAvatar!.isNotEmpty && recordId.isNotEmpty) { + _avatarUrl = + 'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar'; + } else { + _avatarUrl = ''; // Reset to default or empty + } + }); + } catch (e) { + print('Error fetching user details: $e'); + } + } @override Widget build(BuildContext context) { - // Get the current route to highlight the active item String currentRoute = GoRouterState.of(context).matchedLocation; double myheight = MediaQuery.of(context).size.height; double mywidth = MediaQuery.of(context).size.width; - return Scaffold( - appBar: AppBar(title: title, backgroundColor : appbarColor,actions: [ - IconButton(onPressed: () {}, icon: Icon(Icons.toggle_off_outlined)), - ], - leading: - // Stack( - // children: [ - // // Show the back button if showBackButton is true, otherwise show the drawer menu - // if (showBackButton == true) - // Positioned( - // left: 0, - // child: IconButton( - // color: Colors.white, - // icon: Icon(Icons.arrow_back_ios_new), - // onPressed: () { - // Navigator.of(context).pop(); - // }, - // ), - // ), - // // Show the drawer icon in all cases - // Positioned( - // right: 0, - // child: IconButton( - // color: (showBackButton == true)? Colors.white : Colors.black, - // icon: Icon(Icons.menu), - // onPressed: () { - // // Open the drawer using the Scaffold context - // Scaffold.of(context).openDrawer(); - // }, - // ), - // ), - // ], - // ), - Stack( + return Scaffold( + appBar: AppBar( + title: widget.title, + backgroundColor: widget.appbarColor, + actions: widget.actions ?? + [ + IconButton( + onPressed: () {}, + icon: const Icon(Icons.toggle_off_outlined), + ), + ], + leading: Stack( children: [ - // Show the back button if showBackButton is true - if (showBackButton == true) + if (widget.showBackButton == true) Positioned( left: 15, child: IconButton( color: Colors.white, - icon: Icon(Icons.arrow_back_ios_new), + icon: const Icon(Icons.arrow_back_ios_new), onPressed: () { Navigator.of(context).pop(); }, ), ), - // Show the drawer icon in all cases Positioned( right: 10, child: Builder( builder: (BuildContext context) { return IconButton( - color: (showBackButton == true) ? Colors.white : Colors.black, - icon: Icon(Icons.menu), + color: (widget.showBackButton == true) + ? Colors.white + : Colors.black, + icon: const Icon(Icons.menu), onPressed: () { - // Open the drawer using the Scaffold context Scaffold.of(context).openDrawer(); }, ); @@ -92,14 +144,12 @@ class BaseScaffold extends StatelessWidget { ), ), ], - ) - + ), ), drawer: Drawer( child: ListView( children: [ DrawerHeader( - //decoration: BoxDecoration(color: Colors.blue), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -117,18 +167,39 @@ class BaseScaffold extends StatelessWidget { child: Row( children: [ SizedBox( - width: mywidth / 8, - child: Image( - image: AssetImage( - 'assets/edit_profile/profile.png'))), + 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('Mohammad'), - Text('Mohammad@fcsc.com') + // Text(userId ?? 'Loading user...'), // Display userId here + // Text('Mohammad@fcsc.com') + Text(userName ?? 'Loading...'), + Text( + userEmail ?? 'Loading...', + style: TextStyle(fontSize: 12), + ), ], ) ], @@ -138,33 +209,26 @@ class BaseScaffold extends StatelessWidget { ), ), ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/message.png'))), - title: Text('Feedback'), + leading: Icon(Icons.feedback), + title: const Text('Feedback'), onTap: () => context.go('/feedback'), ), + if (role == 'admin') + ListTile( + leading: Icon(Icons.manage_accounts), + title: const Text('Manage User'), + onTap: () => context.go('/manageuser'), + ), ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/manageuser.png'))), - title: Text('Manage User'), - onTap: () => context.go('/manageuser'), - ), - - ListTile( - leading: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/drawer/book.png'))), - title: Text('User Guide'), + leading: Icon(Icons.book), + title: const Text('User Guide'), onTap: () => context.go('/user-guide'), ), + ListTile( + leading: Icon(Icons.logout), + title: const Text('Logout'), + onTap: () => context.go('/'), + ), ], ), ), @@ -174,49 +238,33 @@ class BaseScaffold extends StatelessWidget { type: BottomNavigationBarType.fixed, items: [ BottomNavigationBarItem( - icon: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/bottom_bar/home.png'))), + icon: Icon(Icons.home), label: 'Home', ), BottomNavigationBarItem( - icon: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/bottom_bar/uae_map.png'))), + icon: Icon(Icons.map), label: 'UAE Numbers', ), BottomNavigationBarItem( - icon: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: - AssetImage('assets/icons/bottom_bar/ranking.png'))), - label: 'Competitiveness'), + icon: Icon(Icons.bar_chart), + label: 'Competitiveness', + ), BottomNavigationBarItem( - icon: SizedBox( - height: myheight / 15, - width: mywidth / 15, - child: Image( - image: AssetImage('assets/icons/bottom_bar/globe.png'))), - label: 'Country Profile'), + icon: Icon(Icons.public), + label: 'Country Profile', + ), ], selectedItemColor: Colors.black, unselectedItemColor: Colors.grey, showUnselectedLabels: true, ), - body: body, + body: widget.body, ); } - //Map the current route to the selected index int _getSelectedIndex(String route) { switch (route) { - case '/': + case '/myhomepage': return 0; case '/uaenumbers': return 1; @@ -229,11 +277,10 @@ class BaseScaffold extends StatelessWidget { } } - //Handle navigation when an item is tapped void _onItemTapped(BuildContext context, int index) { switch (index) { case 0: - context.go('/'); + context.go('/myhomepage'); break; case 1: context.go('/uaenumbers'); diff --git a/pubspec.lock b/pubspec.lock index 32cb644e..0b782f6e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -385,10 +385,10 @@ packages: dependency: "direct main" description: name: fl_chart - sha256: "10ddaf334fe84d59333a12d153043e366f243e0bdfff2df0313e1e249f5bf926" + sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08" url: "https://pub.dev" source: hosted - version: "0.70.1" + version: "0.69.2" flutter: dependency: "direct main" description: flutter @@ -440,7 +440,7 @@ packages: source: hosted version: "2.4.4" flutter_plugin_android_lifecycle: - dependency: transitive + dependency: "direct main" description: name: flutter_plugin_android_lifecycle sha256: "615a505aef59b151b46bbeef55b36ce2b6ed299d160c51d84281946f0aa0ce0e" @@ -467,10 +467,10 @@ packages: dependency: "direct main" description: name: flutter_secure_storage - sha256: "1913841ac4c7bf57cd2e05b717e1fbff7841b542962feff827b16525a781b3e4" + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" url: "https://pub.dev" source: hosted - version: "9.2.3" + version: "9.2.4" flutter_secure_storage_linux: dependency: transitive description: @@ -661,10 +661,10 @@ packages: dependency: transitive description: name: image_picker_android - sha256: aa6f1280b670861ac45220cc95adc59bb6ae130259d36f980ccb62220dc5e59f + sha256: b62d34a506e12bb965e824b6db4fbf709ee4589cf5d3e99b45ab2287b008ee0c url: "https://pub.dev" source: hosted - version: "0.8.12+19" + version: "0.8.12+20" image_picker_for_web: dependency: transitive description: @@ -1116,10 +1116,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "02a7d8a9ef346c9af715811b01fbd8e27845ad2c41148eefd31321471b41863d" + sha256: bf808be89fe9dc467475e982c1db6c2faf3d2acf54d526cd5ec37d86c99dbd84 url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" shared_preferences_foundation: dependency: transitive description: @@ -1260,22 +1260,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - syncfusion_flutter_charts: - dependency: "direct main" - description: - name: syncfusion_flutter_charts - sha256: b2a9f0fd585ef96c081c37697b46d48d3b0f3fe6bddc5011a3542962814fafa8 - url: "https://pub.dev" - source: hosted - version: "28.1.33" - syncfusion_flutter_core: - dependency: transitive - description: - name: syncfusion_flutter_core - sha256: "12735505d616320aebe39a6fc90b6608a09116378d66aee9636b0eddf7b75971" - url: "https://pub.dev" - source: hosted - version: "28.1.38" term_glyph: dependency: transitive description: @@ -1376,18 +1360,18 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.4.0" url_launcher_windows: dependency: transitive description: name: url_launcher_windows - sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4" + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" url: "https://pub.dev" source: hosted - version: "3.1.3" + version: "3.1.4" uuid: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 9148bf09..3126af19 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -21,7 +21,7 @@ dependencies: riverpod_annotation: ^2.3.3 cupertino_icons: ^1.0.6 - fl_chart: ^0.70.1 + fl_chart: ^0.69.2 marquee: ^2.2.3 url_launcher: ^6.3.1 @@ -57,9 +57,9 @@ dependencies: provider: ^6.1.2 mailer: ^6.2.0 image_picker: ^1.1.2 - syncfusion_flutter_charts: 28.1.33 material_charts: ^0.0.23 flutter_staggered_grid_view: ^0.7.0 + flutter_plugin_android_lifecycle: ^2.0.24 dependency_overrides: fading_edge_scrollview: ^4.1.1