app code add

This commit is contained in:
Kalonkarthik 2025-01-29 19:33:04 +05:30
commit 920a1d3aa9
11 changed files with 494 additions and 281 deletions

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
<option name="filePath" value="$PROJECT_DIR$/lib/main.dart" />
<option name="filePath" value="$PROJECT_DIR$/frontend/lib/main.dart" />
<method v="2" />
</configuration>
</component>

View File

@ -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 {

View File

@ -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

View File

@ -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"
// }
// ]

View File

@ -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<LineChartBarData> lineBarsData(dynamic chartData) {
List<LineChartBarData> lineBars = [];
// Filter data for males and females
List<Map<String, dynamic>> maleData = [];
List<Map<String, dynamic>> 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<FlSpot> maleSpots = maleData.map<FlSpot>((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<FlSpot> femaleSpots = femaleData.map<FlSpot>((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<Color> uniqueColors = [
Color(0xFF648CBA),
@ -493,7 +614,7 @@ class ChartWidget extends StatelessWidget {
}
List<BarChartGroupData> _generateBarGroups(
dynamic chartData, String groupByKey) {
BuildContext context, dynamic chartData, String groupByKey) {
Map<String, Map<String, double>> 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<String> 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;

View File

@ -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<EditProfile> createState() => _EditProfileState();
}
@ -31,7 +33,7 @@ class _EditProfileState extends State<EditProfile> {
true,
true,
true,
true
true,
]; // One for each TextField
String _getControllerText(int index) {
@ -57,7 +59,7 @@ class _EditProfileState extends State<EditProfile> {
'United Arab Emirates',
'United States',
'India',
'Canada'
'Canada',
];
String? _selectedCountry;
bool isChecked = false;
@ -65,11 +67,15 @@ class _EditProfileState extends State<EditProfile> {
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<RecordModel> userDetails;
dynamic userId;
dynamic recordId;
dynamic collectionId;
dynamic role;
@override
void initState() {
@ -147,16 +153,23 @@ class _EditProfileState extends State<EditProfile> {
Future<void> _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<EditProfile> {
// 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<EditProfile> {
// 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<EditProfile> {
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<EditProfile> {
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<EditProfile> {
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<EditProfile> {
// 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<EditProfile> {
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<EditProfile> {
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<EditProfile> {
// ),
// ),
// 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<EditProfile> {
),
),
),
);
}
},
)
: 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<Color>(
Colors.grey),
Colors.grey,),
),
),
],
@ -528,9 +590,9 @@ class _EditProfileState extends State<EditProfile> {
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<EditProfile> {
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<EditProfile> {
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<EditProfile> {
//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<EditProfile> {
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<EditProfile> {
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<EditProfile> {
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<EditProfile> {
),
items: _countries
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(item),
))
value: item,
child: Text(item),
),)
.toList(),
onChanged: (String? newValue) {
setState(() {
@ -697,16 +758,16 @@ class _EditProfileState extends State<EditProfile> {
},
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<EditProfile> {
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<EditProfile> {
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<EditProfile> {
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<EditProfile> {
}
}
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,
)
),
],
);
}
}
}

View File

@ -213,7 +213,7 @@ class _FeedbackFormState extends State<FeedbackForm>
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,

View File

@ -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<Widget>? 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<Widget>? actions;
final Color? appbarColor, mytitleColor;
@override
_BaseScaffoldState createState() => _BaseScaffoldState();
@ -56,6 +59,7 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
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<BaseScaffold> {
];
}
// Method to retrieve userId from SharedPreferences
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('userId'); // Retrieve the userId
@ -507,125 +510,39 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
userId = fetchedUserId;
});
//print('NAVUser ID: $userId');
_initializeUserData();
_fetchUserData();
} else {
print('No userId found');
// Handle the case where userId is not available
}
}
// Future<void> _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<void> _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<String, dynamic> 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<void> _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<BaseScaffold> {
Future<void> logout() async {
final prefs = await SharedPreferences.getInstance();
// setState(() {
// isLoggedOut = true;
// });
prefs.clear();
context.go('/');
}
@ -666,9 +586,10 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
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<BaseScaffold> {
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<BaseScaffold> {
)
: 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<BaseScaffold> {
style: TextStyle(fontSize: 12),
),
],
)
),
],
),
)
),
],
),
),
@ -792,7 +716,6 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
),
),
bottomNavigationBar: BottomNavigationBar(
key: bottomNavKey,
currentIndex: _getSelectedIndex(currentRoute),
onTap: (index) => _onItemTapped(context, index),
type: BottomNavigationBarType.fixed,

View File

@ -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"))

View File

@ -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:

View File

@ -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