diff --git a/.run/main.dart.run.xml b/.run/main.dart.run.xml index 4767aff8..4a58a7c9 100644 --- a/.run/main.dart.run.xml +++ b/.run/main.dart.run.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index ec52a38b..49fcb1cd 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -29,8 +29,12 @@ android { // ndkVersion = flutter.ndkVersion ndkVersion = "25.1.8937393" compileOptions { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" } defaultConfig { diff --git a/android/gradle.properties b/android/gradle.properties index 303728bd..476c2b1c 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,4 @@ org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true -org.gradle.java.home=C:/Program Files/Eclipse Adoptium/jdk-21.0.5.11-hotspot +org.gradleorg.gradle.java.home=/usr/lib/jvm/openlogic-openjdk-17.0.13+11-linux-x64 diff --git a/lib/presentation/Screens/charts/chart.dart b/lib/presentation/Screens/charts/chart.dart index 5c35d379..a18a0194 100644 --- a/lib/presentation/Screens/charts/chart.dart +++ b/lib/presentation/Screens/charts/chart.dart @@ -1035,49 +1035,47 @@ // // [ // { -// chart_heading :"Guest Nights by Region" -// chart_type :"fl_stacked_bar" -// dataset :"hotels" -// group_by :"GUEST_REGION" +// chart_heading :"Population Growth Over Time" +// chart_type :"line_trend" +// dataset :"population" +// group_by :"GENDER" // is_chart :"true" -// kpi :"hotel_occupancy_rate_chart_1" -// main_id :"economy" +// kpi :"population" +// main_id :"social" // response:[ // { // "ObsKey": { // "FREQ": "A", -// "GUEST_REGION": "AF", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", +// "GENDER": "M", +// "MEASURE": "POP", +// "POP_IND": "_Z", // "REF_AREA": "AE", // "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2016", -// "UNIT_MEASURE": "NUMBER" +// "TIME_PERIOD": "1970", +// "UNIT_MEASURE": "PS" // }, // "ObsValue": { -// "Value": "2659446" +// "Value": "149195" +// } // } -// }, // { // "ObsKey": { // "FREQ": "A", -// "GUEST_REGION": "OC", -// "H_INDICATOR": "GUN", -// "H_TYPE": "_Z", -// "MEASURE": "H", +// "GENDER": "F", +// "MEASURE": "POP", +// "POP_IND": "_Z", // "REF_AREA": "AE", // "SOURCE_DETAIL": "FCSC", -// "TIME_PERIOD": "2018", -// "UNIT_MEASURE": "NUMBER" +// "TIME_PERIOD": "1995", +// "UNIT_MEASURE": "PS" // }, // "ObsValue": { -// "Value": "1127282" +// "Value": "850654" // } // } // // ] -// 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" +// sub_id :"population" +// url :"https://releaseeuaestat.fcsc.gov.ae/rest/data/FCSA,DF_POP,2.7.0/....A..?startPeriod=1970&dimensionAtObservation=AllDimensions" // } // ] diff --git a/lib/presentation/Screens/charts/widgets/chart_widget.dart b/lib/presentation/Screens/charts/widgets/chart_widget.dart index 8061a39a..5e24182b 100644 --- a/lib/presentation/Screens/charts/widgets/chart_widget.dart +++ b/lib/presentation/Screens/charts/widgets/chart_widget.dart @@ -108,7 +108,7 @@ class ChartWidget extends StatelessWidget { }).toSet(); } - Widget buildChart(dynamic chartData) { + Widget buildChart(dynamic chartData, BuildContext context) { print('chartDataccccccc $chartData'); // Extract group_by dynamically from the chartData if (chartData == null || chartData['response'] == null) { @@ -379,7 +379,7 @@ class ChartWidget extends StatelessWidget { ), gridData: FlGridData(show: false), borderData: FlBorderData(show: false), - barGroups: _generateBarGroups(chartData, groupByKey), + barGroups: _generateBarGroups(context, chartData, groupByKey), ), ), ), @@ -432,6 +432,17 @@ class ChartWidget extends StatelessWidget { ) ], ); + case 'line_trend': + case 'line_trend': + return LineChart(LineChartData( + lineTouchData: lineTouchData1(), + gridData: gridData(), + titlesData: titlesData1(), + borderData: borderData(), + lineBarsData: lineBarsData(chartData), + minX: 1970, // Adjust based on your data range + maxX: 2025, // Adjust based on your data range + )); case 'fl_multi_bar': return BarChart( BarChartData( @@ -477,6 +488,116 @@ class ChartWidget extends StatelessWidget { } } +// Function to create LineChartBarData for Males and Females + List lineBarsData(dynamic chartData) { + List lineBars = []; + + // Filter data for males and females + List> maleData = []; + List> femaleData = []; + + // Separate the data based on gender + for (var entry in chartData['response']) { + if (entry['ObsKey']['GENDER'] == 'M') { + maleData.add(entry); + } else if (entry['ObsKey']['GENDER'] == 'F') { + femaleData.add(entry); + } + } + + // Prepare data for Male + List maleSpots = maleData.map((entry) { + double xValue = double.parse(entry['ObsKey']['TIME_PERIOD']); + double yValue = double.parse(entry['ObsValue']['Value']); + return FlSpot(xValue, yValue); + }).toList(); + + // Prepare data for Female + List femaleSpots = femaleData.map((entry) { + double xValue = double.parse(entry['ObsKey']['TIME_PERIOD']); + double yValue = double.parse(entry['ObsValue']['Value']); + return FlSpot(xValue, yValue); + }).toList(); + + // Add the Male line (Blue color) + lineBars.add( + LineChartBarData( + spots: maleSpots, + isCurved: true, + color: Colors.blue, + barWidth: 3, + belowBarData: BarAreaData(show: false), + ), + ); + + // Add the Female line (Pink color) + lineBars.add( + LineChartBarData( + spots: femaleSpots, + isCurved: true, + color: Colors.pink, + barWidth: 3, + belowBarData: BarAreaData(show: false), + ), + ); + + return lineBars; + } + +// Sample implementation for other chart details like titles, grid, etc. + LineTouchData lineTouchData1() { + return LineTouchData( + touchTooltipData: LineTouchTooltipData( + // tooltipBgColor: Colors.blueAccent, + ), + // touchCallback: (LineTouchResponse touchResponse) {}, + handleBuiltInTouches: true, + ); + } + + FlGridData gridData() { + return FlGridData( + show: true, + drawVerticalLine: true, + getDrawingHorizontalLine: (value) => + FlLine(color: Colors.grey, strokeWidth: 1), + getDrawingVerticalLine: (value) => + FlLine(color: Colors.grey, strokeWidth: 1), + ); + } + + FlTitlesData titlesData1() { + return FlTitlesData( + leftTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + return Text( + value.toInt().toString(), + style: TextStyle(color: Colors.black, fontSize: 12), + ); + }, + ), + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + getTitlesWidget: (value, meta) { + return Text( + value.toInt().toString(), + style: TextStyle(color: Colors.black, fontSize: 12), + ); + }, + ), + ), + ); + } + + FlBorderData borderData() { + return FlBorderData( + show: true, border: Border.all(color: Colors.black, width: 1)); + } + // Define the list of unique colors final List uniqueColors = [ Color(0xFF648CBA), @@ -493,7 +614,7 @@ class ChartWidget extends StatelessWidget { } List _generateBarGroups( - dynamic chartData, String groupByKey) { + BuildContext context, dynamic chartData, String groupByKey) { Map> groupedData = {}; // Group data by `group_by` and `TIME_PERIOD`, accumulating the values for each group and year @@ -521,6 +642,8 @@ class ChartWidget extends StatelessWidget { List timePeriods = groupedData.values.first.keys.toList(); print('timePeriods $timePeriods'); + double barWidth = calculateBarWidth(context, timePeriods.length); + // For each time period, generate a BarChartGroupData for (int i = 0; i < timePeriods.length; i++) { // Intermediate variable to track the current toY value as we stack bars @@ -555,7 +678,8 @@ class ChartWidget extends StatelessWidget { BarChartRodData( toY: currentToY, // Use the accumulated `currentToY` value rodStackItems: rodStackItems, // Add the stacked items - width: 20, + // width: 20, + width: barWidth, borderRadius: BorderRadius.zero, color: Colors .transparent, // This will act as a container for stacked items @@ -644,10 +768,36 @@ class ChartWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return buildChart(chartData); + return buildChart(chartData, context); } } +double calculateBarWidth(BuildContext context, int totalBars) { + double width = MediaQuery.of(context).size.width; // Chart width + double padding = 16.0; // Space between bars + double availableWidth = (width - 100); + if (totalBars <= 0) { + throw ArgumentError("TotalBars must be greater than 0"); + } + + double barWidth = (availableWidth - (padding * (totalBars - 1))) / totalBars; + + print('barWidth - $barWidth'); + + // Ensure bar width does not exceed availableWidth + if (barWidth > availableWidth) { + barWidth = availableWidth; + } + + // Optional: Add a minimum width constraint if needed + double minWidth = 10.0; // Example minimum width + if (barWidth < minWidth) { + barWidth = minWidth; + } + + return barWidth; +} + class ChartData { final String? x; final double y; 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 32782d42..fbaff1ef 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/edit_profile.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:io'; + import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -11,10 +12,11 @@ import 'package:intl/intl.dart'; import 'package:pocketbase/pocketbase.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; -import '../../../Screens/auth_verification/changepassword.dart'; -import '../custom_drawer_routes.dart'; +import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; class EditProfile extends StatefulWidget { + const EditProfile({super.key}); + @override State createState() => _EditProfileState(); } @@ -31,7 +33,7 @@ class _EditProfileState extends State { true, true, true, - true + true, ]; // One for each TextField String _getControllerText(int index) { @@ -57,7 +59,7 @@ class _EditProfileState extends State { 'United Arab Emirates', 'United States', 'India', - 'Canada' + 'Canada', ]; String? _selectedCountry; bool isChecked = false; @@ -65,11 +67,15 @@ class _EditProfileState extends State { final _picker = ImagePicker(); File? _profileImage; String _avatarUrl = ''; + bool isPageLoad= false; // Regular expression to validate Full Name (no special characters) - final RegExp _nameRegExp = RegExp(r"^[a-zA-Z\s]+$"); + final RegExp _nameRegExp = RegExp(r'^[a-zA-Z\s]+$'); late Future userDetails; dynamic userId; + dynamic recordId; + dynamic collectionId; + dynamic role; @override void initState() { @@ -147,16 +153,23 @@ class _EditProfileState extends State { Future _fetchUserData() async { try { + setState(() { + isPageLoad = true; // Show loader + print('Im isPageLoad'); + }); + print('EDIT PROFILE isPageLoad'); final adminAuth = await _pb.admins .authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com'); final adminToken = adminAuth.token; - print('adminToken- ${adminToken}'); + print('adminToken- $adminToken'); final userDetailsResponse = await _pb.collection('users').getOne( userId, headers: { 'Authorization': 'Bearer $adminToken', }, ); + + print('userDetails: $userDetailsResponse'); setState(() { _usernameController.text = userDetailsResponse.data['uname'] ?? ''; @@ -179,16 +192,18 @@ class _EditProfileState extends State { // Set the avatar URL String avatarFilename = userDetailsResponse.data['avatar'] ?? ''; - String recordId = userId; - String collectionId = + recordId = userId; + collectionId = userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_'; + role = userDetailsResponse.data['role']; if (avatarFilename.isNotEmpty && recordId.isNotEmpty) { _avatarUrl = - 'https://pb.venbait.in/api/files/$collectionId/$recordId/$avatarFilename'; + 'https://pb.venbait.in/api/files/$collectionId/$recordId/$avatarFilename'; } else { _avatarUrl = ''; // Reset to default or empty } + isPageLoad = false; }); } catch (e) { print('Error fetching user details: $e'); @@ -217,19 +232,19 @@ class _EditProfileState extends State { // await Future.delayed(Duration(seconds: 8)); // Example delay final XFile? pickedFile = - await _picker.pickImage(source: ImageSource.gallery); + await _picker.pickImage(source: ImageSource.gallery); if (pickedFile != null) { setState(() { _isLoading = true; // Start loading }); print(pickedFile); final String fileExtension = - pickedFile.path.split('.').last.toLowerCase(); + pickedFile.path.split('.').last.toLowerCase(); print('fileExtension $fileExtension'); if (fileExtension == 'jpg' || fileExtension == 'jpeg' || fileExtension == 'png' || - fileExtension == "heic") { + fileExtension == 'heic') { setState(() { _isLoading = false; _profileImage = File(pickedFile.path); @@ -250,13 +265,13 @@ class _EditProfileState extends State { final DateTime today = DateTime.now(); final DateTime initialDate = _selectedDate ?? today.subtract( - const Duration(days: 365 * 18)); // Default to 18 years ago + const Duration(days: 365 * 18),); // Default to 18 years ago final DateTime firstDate = today.subtract(const Duration( - days: 365 * 100)); // Allow picking dates back to 100 years ago + days: 365 * 100,),); // Allow picking dates back to 100 years ago final DateTime lastDate = today; // Allow picking dates up to today // Updated Date format to DD/MM/YYYY - final DateFormat _dateFormat = DateFormat('dd/MM/yyyy'); + final DateFormat dateFormat = DateFormat('dd/MM/yyyy'); final DateTime? pickedDate = await showDatePicker( context: context, @@ -268,7 +283,7 @@ class _EditProfileState extends State { if (pickedDate != null && pickedDate != _selectedDate) { setState(() { _selectedDate = pickedDate; - _dateController.text = _dateFormat.format(pickedDate); + _dateController.text = dateFormat.format(pickedDate); }); } } @@ -314,26 +329,26 @@ class _EditProfileState extends State { try { String userID = userId; - print("ShowConfirmationuserID - $userID "); + print('ShowConfirmationuserID - $userID '); // Retrieve data from the country/region field String countryRegion = _selectedCountry ?? ''; // Ensure the country is selected // Create a multipart request final uri = - Uri.parse('${_pb.baseUrl}/api/collections/users/records/$userID'); + Uri.parse('${_pb.baseUrl}/api/collections/users/records/$userID'); final request = http.MultipartRequest('PATCH', uri); // Add fields to the request - request.fields['country_region'] = - countryRegion; // Only update country here + request.fields['country_region'] = countryRegion; // Only update country here // If profile image exists, add it if (_profileImage != null) { request.files.add(await http.MultipartFile.fromPath( 'avatar', _profileImage!.path, - )); + ),); + } // Add headers (e.g., authorization) @@ -341,25 +356,27 @@ class _EditProfileState extends State { // Send the request final response = await request.send(); - print(response); - + // print(response); + print('ShowConfirmationuser response - $response '); // Handle response if (response.statusCode == 200) { + _resetFormFields(); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Profile updated successfully!")), + SnackBar(content: Text('Profile updated successfully!')), ); + // print('ShowConfirmation userData - $userData '); context.go('/myhomepage'); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: - Text("Failed to update profile: ${response.statusCode}")), + content: + Text('Failed to update profile: ${response.statusCode}'),), ); } } catch (error) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("Failed to update profile: $error")), + SnackBar(content: Text('Failed to update profile: $error')), ); } } else { @@ -392,9 +409,9 @@ class _EditProfileState extends State { 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"); + print('Image loaded from URL: $url'); } catch (e) { - print("Error loading image: $e"); + print('Error loading image: $e'); throw Exception('Failed to load image'); } } @@ -413,7 +430,11 @@ class _EditProfileState extends State { key: _formKey, child: Padding( padding: const EdgeInsets.all(20.0), - child: Column( + child: isPageLoad + ? Center( + child: CircularProgressIndicator(), + ) + : Column( children: [ // CircleAvatar( // radius: 50, @@ -446,58 +467,81 @@ class _EditProfileState extends State { // ), // ), + + + // Stack( + // alignment: Alignment.center, + // children: [ + // // FutureBuilder to load the image + // FutureBuilder( + // future: _loadProfileImage(), + // builder: (context, snapshot) { + // if (snapshot.connectionState == ConnectionState.waiting) { + // // While the image is loading, show a progress indicator + // return CircleAvatar( + // radius: 50, + // child: CircularProgressIndicator(), + // ); + // } else if (snapshot.hasError || snapshot.data == null) { + // // If there's an error or no image, show an error icon + // return CircleAvatar( + // radius: 50, + // child: CircularProgressIndicator(), + // ); + // } else { + // // Display the loaded image + // return CircleAvatar( + // radius: 50, + // backgroundImage: snapshot.data, + // child: Align( + // alignment: Alignment.bottomRight, + // child: GestureDetector( + // onTap: _pickImage, + // 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 + ? FileImage(_profileImage!) // If a local file is selected : _avatarUrl.isNotEmpty - ? NetworkImage( - _avatarUrl) // Load from URL - : AssetImage( - "assets/edit_profile/profile.png") - as ImageProvider, + ? 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( + 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 + onTap: _pickImage, // Call `_pickImage` on tap child: CircleAvatar( radius: 15, backgroundColor: Colors.white, @@ -508,14 +552,32 @@ class _EditProfileState extends State { ), ), ), + ); + } + }, + ) + : 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), + Colors.grey,), ), ), ], @@ -528,9 +590,9 @@ class _EditProfileState extends State { Text( AppLocalizations.of(context)!.register_name, style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.grey), + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.grey,), ), ], ), @@ -554,9 +616,9 @@ class _EditProfileState extends State { Text( AppLocalizations.of(context)!.email_id, style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.grey), + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.grey,), ), ], ), @@ -579,9 +641,9 @@ class _EditProfileState extends State { Text( AppLocalizations.of(context)!.full_name, style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.grey), + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.grey,), ), ], ), @@ -595,7 +657,7 @@ class _EditProfileState extends State { //RegExp(r"^[a-zA-Z\s]+$"); final nameRegex = RegExp( - r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$"); + r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$",); if (!nameRegex.hasMatch(value)) { return 'Invalid Characters'; } @@ -613,7 +675,7 @@ class _EditProfileState extends State { enabled: !_isProfileCompleted, ), maxLength: - 40, // Set the maximum length to 20 characters + 40, // Set the maximum length to 20 characters maxLengthEnforcement: MaxLengthEnforcement.enforced, ), SizedBox(height: 10), @@ -621,12 +683,11 @@ class _EditProfileState extends State { mainAxisAlignment: MainAxisAlignment.start, children: [ Text( - // "Date of Birth*", - AppLocalizations.of(context)!.dob, + 'Date of Birth*', style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Colors.grey), + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.grey,), ), ], ), @@ -654,7 +715,7 @@ class _EditProfileState extends State { Text( AppLocalizations.of(context)!.region, style: TextStyle( - fontSize: 16, fontWeight: FontWeight.w500), + fontSize: 16, fontWeight: FontWeight.w500,), ), ], ), @@ -669,9 +730,9 @@ class _EditProfileState extends State { ), items: _countries .map((item) => DropdownMenuItem( - value: item, - child: Text(item), - )) + value: item, + child: Text(item), + ),) .toList(), onChanged: (String? newValue) { setState(() { @@ -697,16 +758,16 @@ class _EditProfileState extends State { }, side: BorderSide( color: - showError ? Colors.red : Colors.grey, + showError ? Colors.red : Colors.grey, width: 1.5, ), ), Expanded( child: Column( mainAxisAlignment: - MainAxisAlignment.start, + MainAxisAlignment.start, crossAxisAlignment: - CrossAxisAlignment.start, + CrossAxisAlignment.start, children: [ SizedBox(height: 10), Text.rich( @@ -714,51 +775,51 @@ class _EditProfileState extends State { text: AppLocalizations.of(context)! .agree, style: - TextStyle(color: Colors.black), + TextStyle(color: Colors.black), children: [ TextSpan( text: AppLocalizations.of( - context)! + context,)! .terms_conditions, style: TextStyle( color: Colors.blue, decoration: - TextDecoration.underline, + TextDecoration.underline, ), recognizer: - TapGestureRecognizer() - ..onTap = () { - // Add action for Terms & Conditions tap - }, + TapGestureRecognizer() + ..onTap = () { + // Add action for Terms & Conditions tap + }, ), TextSpan( text: AppLocalizations.of( - context)! + context,)! .t_and, style: TextStyle( - color: Colors.black), + color: Colors.black,), ), TextSpan( text: AppLocalizations.of( - context)! + context,)! .privacy_policy, style: TextStyle( color: Colors.blue, decoration: - TextDecoration.underline, + TextDecoration.underline, ), recognizer: - TapGestureRecognizer() - ..onTap = () { - // Add action for Privacy Policy tap - }, + TapGestureRecognizer() + ..onTap = () { + // Add action for Privacy Policy tap + }, ), TextSpan( text: AppLocalizations.of( - context)! + context,)! .conditions, style: TextStyle( - color: Colors.black), + color: Colors.black,), ), ], ), @@ -778,7 +839,7 @@ class _EditProfileState extends State { children: [ Padding( padding: - const EdgeInsets.only(left: 10.0), + const EdgeInsets.only(left: 10.0), child: Text( 'Please agree to terms and conditions', style: TextStyle( @@ -801,8 +862,8 @@ class _EditProfileState extends State { child: Text( AppLocalizations.of(context)!.change_password, style: TextStyle( - color: Colors.blue, - decoration: TextDecoration.underline), + color: Colors.blue, + decoration: TextDecoration.underline,), ), ), ), @@ -841,8 +902,10 @@ class _EditProfileState extends State { } } + + class ConfirmationDialog extends StatelessWidget { - const ConfirmationDialog({Key? key}) : super(key: key); + const ConfirmationDialog({super.key}); @override Widget build(BuildContext context) { return AlertDialog( @@ -850,7 +913,7 @@ class ConfirmationDialog extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - "Are you sure you want to save this page?", + 'Are you sure you want to save this page?', style: const TextStyle( fontSize: 14, color: Color(0xFF898C81), @@ -862,7 +925,7 @@ class ConfirmationDialog extends StatelessWidget { SizedBox(height: 8), Text.rich( TextSpan( - text: "Once saved, you will not be able to change your ", + text: 'Once saved, you will not be able to change your ', style: TextStyle( fontSize: 14, color: Color(0xFF898C81), @@ -871,25 +934,25 @@ class ConfirmationDialog extends StatelessWidget { ), children: [ TextSpan( - text: "Country", + text: 'Country', style: - TextStyle(fontWeight: FontWeight.w700), // Bold for "name" + TextStyle(fontWeight: FontWeight.w700), // Bold for "name" ), TextSpan( - text: " or ", + text: ' or ', ), TextSpan( - text: "Profile Image", + text: 'Profile Image', style: TextStyle( - fontWeight: FontWeight.w700), // Bold for "date of birth" + fontWeight: FontWeight.w700,), // Bold for "date of birth" ), TextSpan( - text: ".", + text: '.', ), ], ), textAlign: TextAlign.center, - ) + ), ], ), shape: RoundedRectangleBorder( @@ -914,7 +977,7 @@ class ConfirmationDialog extends StatelessWidget { ), // Set the border color here ), child: Text( - "Cancel", + 'Cancel', style: TextStyle(color: Color(0xFF92722A)), ), ), @@ -939,8 +1002,8 @@ class ConfirmationDialog extends StatelessWidget { ), SizedBox( height: 5, - ) + ), ], ); } -} +} \ No newline at end of file diff --git a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart index 83ac097e..2c701b8a 100644 --- a/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart +++ b/lib/presentation/routes/drawer_routes/Drawer Items/feedback.dart @@ -213,7 +213,7 @@ class _FeedbackFormState extends State title: Text(AppLocalizations.of(context)!.feedback_title), // appBar: AppBar( // backgroundColor: const Color(0xFFf8f9ff), - // title: Text(context.translate('Feedback Form', 'نموذج الملاحظات')), + // title: Text(AppLocalizations.of(context)!.feedback_title), // actions: [ // Align( // alignment: AlignmentDirectional.topEnd, diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index 1d13c05d..92e0ad82 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -1,5 +1,7 @@ +import 'dart:async'; import 'dart:convert'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; @@ -8,20 +10,15 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; +import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; +import 'package:uae_stat/presentation/components/my_toggle.dart'; import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart'; import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; import 'package:uae_stat/presentation/components/my_toggle.dart'; -class BaseScaffold extends ConsumerStatefulWidget { - final Widget body; - final Widget title; - final bool? showBackButton; - final bool? mycenterTitle; - final List? actions; - final Color? appbarColor, mytitleColor; - +class BaseScaffold extends ConsumerStatefulWidget { const BaseScaffold({ - Key? key, + super.key, required this.body, required this.title, this.actions, @@ -29,7 +26,13 @@ class BaseScaffold extends ConsumerStatefulWidget { this.mycenterTitle = false, this.appbarColor, this.mytitleColor, - }) : super(key: key); + }); + final Widget body; + final Widget title; + final bool? showBackButton; + final bool? mycenterTitle; + final List? actions; + final Color? appbarColor, mytitleColor; @override _BaseScaffoldState createState() => _BaseScaffoldState(); @@ -56,6 +59,7 @@ class _BaseScaffoldState extends ConsumerState { final GlobalKey toggleKey = GlobalKey(); final GlobalKey drawerKey = GlobalKey(); late double toggleHeight=0; + bool isLoggedOut = false; @override void initState() { @@ -493,7 +497,6 @@ class _BaseScaffoldState extends ConsumerState { ]; } - // Method to retrieve userId from SharedPreferences Future getUserId() async { final prefs = await SharedPreferences.getInstance(); return prefs.getString('userId'); // Retrieve the userId @@ -507,125 +510,39 @@ class _BaseScaffoldState extends ConsumerState { userId = fetchedUserId; }); //print('NAVUser ID: $userId'); - _initializeUserData(); + _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'); - // } - // } - - Future _initializeUserData() async { - print("Laoding 1"); - final prefs = await SharedPreferences.getInstance(); - // final userDataString = prefs.getString('userData'); - - if (prefs.containsKey('userData')) { - // print("Loading user data from local storage: $userData"); - final String? userDataString = prefs.getString('userData'); - - if (userDataString != null) { - // Decode the JSON string into a Map - final Map userData = jsonDecode(userDataString); - - // Access the userName field - setState(() { - userName = userData['userNames']; - userEmail = userData['userEmails']; - userAvatar = userData['userAvatars']; - role = userData['roles']; - String recordId = userId; - String collectionId = userData['collectionIds']; - - if (userAvatar!.isNotEmpty && recordId.isNotEmpty) { - _avatarUrl = - 'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar'; - } else { - _avatarUrl = ''; // Reset to default or empty - } - }); - } else { - print("No user data found in local storage."); - } - } else { - // Fetch data from server and save it to local storage - - await _fetchUserData(); - } - } - Future _fetchUserData() async { try { - // print("Fetching _fetchUserData"); 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('User details fetched from server: $userDetailsResponse'); - - final userData = { - 'userNames': userDetailsResponse.data['uname'] ?? '', - 'userEmails': userDetailsResponse.data['email'] ?? '', - 'userAvatars': userDetailsResponse.data['avatar'] ?? '', - 'roles': userDetailsResponse.data['role'] ?? '', - 'collectionIds': - userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_', - }; - - final prefs = await SharedPreferences.getInstance(); - await prefs.setString('userData', jsonEncode(userData)); + print('NAVuserDetails: $userDetailsResponse'); setState(() { - userNames = userData['userNames']; - userEmails = userData['userEmails']; - userAvatars = userData['userAvatars']; - roles = userData['roles']; + userName = userDetailsResponse.data['uname'] ?? ''; + userEmail = userDetailsResponse.data['email'] ?? ''; + userAvatar = userDetailsResponse.data['avatar'] ?? ''; + role = userDetailsResponse.data['role'] ?? ''; String recordId = userId; - collectionIds = userData['collectionIds']; + String collectionId = + userDetailsResponse.data['collectionId'] ?? '_pb_users_auth_'; - if (userAvatars!.isNotEmpty && recordId.isNotEmpty) { + if (userAvatar!.isNotEmpty && recordId.isNotEmpty) { _avatarUrl = - 'https://pb.venbait.in/api/files/$collectionIds/$recordId/$userAvatars'; + 'https://pb.venbait.in/api/files/$collectionId/$recordId/$userAvatar'; } else { _avatarUrl = ''; // Reset to default or empty } @@ -637,6 +554,9 @@ class _BaseScaffoldState extends ConsumerState { Future logout() async { final prefs = await SharedPreferences.getInstance(); + // setState(() { + // isLoggedOut = true; + // }); prefs.clear(); context.go('/'); } @@ -666,9 +586,10 @@ class _BaseScaffoldState extends ConsumerState { knobTextWhenOff: 'EN', pathColorWhenOn: Colors.grey.shade300, pathColorWhenOff: Colors.grey.shade300, - onTap: (){ + onTap: () { ref.read(localeProvider.notifier).toggleLocale(); - },), + }, + ), ], leading: Stack( children: [ @@ -713,9 +634,11 @@ class _BaseScaffoldState extends ConsumerState { Row( children: [ SizedBox( - width: mywidth / 8, - child: Image( - image: AssetImage('assets/logos/fcsc.png'))), + width: mywidth / 8, + child: Image( + image: AssetImage('assets/logos/fcsc.png'), + ), + ), ], ), Divider(), @@ -736,7 +659,8 @@ class _BaseScaffoldState extends ConsumerState { ) : Image( image: AssetImage( - 'assets/edit_profile/profile.png'), + 'assets/edit_profile/profile.png', + ), fit: BoxFit.cover, width: double.infinity, height: double.infinity, @@ -758,10 +682,10 @@ class _BaseScaffoldState extends ConsumerState { style: TextStyle(fontSize: 12), ), ], - ) + ), ], ), - ) + ), ], ), ), @@ -792,7 +716,6 @@ class _BaseScaffoldState extends ConsumerState { ), ), bottomNavigationBar: BottomNavigationBar( - key: bottomNavKey, currentIndex: _getSelectedIndex(currentRoute), onTap: (index) => _onItemTapped(context, index), type: BottomNavigationBarType.fixed, diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 1c9c8dd7..8a156355 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -11,6 +11,7 @@ import flutter_secure_storage_macos import path_provider_foundation import share_plus import shared_preferences_foundation +import sqflite_darwin import url_launcher_macos import video_player_avfoundation import webview_flutter_wkwebview @@ -22,6 +23,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin")) diff --git a/pubspec.lock b/pubspec.lock index be76549b..c1d2fc52 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -134,6 +134,30 @@ packages: url: "https://pub.dev" source: hosted version: "8.9.3" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" characters: dependency: transitive description: @@ -409,15 +433,23 @@ packages: dependency: "direct main" description: name: fl_chart - sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08" + sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237" url: "https://pub.dev" source: hosted - version: "0.69.2" + version: "0.70.2" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" flutter_carousel_widget: dependency: "direct main" description: @@ -1252,6 +1284,46 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709" + url: "https://pub.dev" + source: hosted + version: "2.5.4+6" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c" + url: "https://pub.dev" + source: hosted + version: "2.4.1+1" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6caf3e5f..90e0f094 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -59,6 +59,8 @@ dependencies: image_picker: ^1.1.2 material_charts: ^0.0.23 flutter_staggered_grid_view: ^0.7.0 + flutter_plugin_android_lifecycle: ^2.0.24 + cached_network_image: ^3.4.1 syncfusion_flutter_charts: ^28.1.39 connectivity_plus: ^6.1.2 @@ -110,7 +112,6 @@ flutter: - assets/cached_responses/sharepoint/country_leader_imgs/ - assets/edit_profile/ - assets/splash_screen/ - - assets/app_tour/ fonts: - family: Segoe