App Tour Bug and Bookmark Frontend added
@ -15,12 +15,12 @@ if (localPropertiesFile.exists()) {
|
||||
|
||||
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
|
||||
if (flutterVersionCode == null) {
|
||||
flutterVersionCode = "3"
|
||||
flutterVersionCode = "6"
|
||||
}
|
||||
|
||||
def flutterVersionName = localProperties.getProperty("flutter.versionName")
|
||||
if (flutterVersionName == null) {
|
||||
flutterVersionName = "1.0.2"
|
||||
flutterVersionName = "1.0.5"
|
||||
}
|
||||
|
||||
def keystorePropertiesFile = rootProject.file("key.properties")
|
||||
|
||||
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 19 KiB |
BIN
assets/icons/uae_numbers/bookmarks.png
Normal file
|
After Width: | Height: | Size: 325 B |
BIN
assets/icons/uae_numbers/share.png
Normal file
|
After Width: | Height: | Size: 375 B |
|
Before Width: | Height: | Size: 558 B After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 19 KiB |
@ -414,7 +414,9 @@ final GoRouter router = GoRouter(
|
||||
builder: (context, state) {
|
||||
final userId = state.pathParameters['userId']!;
|
||||
final email = state.pathParameters['email']!;
|
||||
return CreateNewPw(userId: userId, email: email);
|
||||
final key = state.uri.queryParameters['key'] ?? '';
|
||||
|
||||
return CreateNewPw(userId: userId, email: email, keyParam: key);
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class UaeNumbersAssetPath {
|
||||
static const _basePath = 'assets/icons/uae_numbers';
|
||||
static const bookmarksUae = '$_basePath/bookmarks.png';
|
||||
static const shareUae = '$_basePath/share.png';
|
||||
}
|
||||
@ -13,7 +13,9 @@ import '../../../config/my_theme.dart';
|
||||
class CreateNewPw extends StatefulWidget {
|
||||
final String userId;
|
||||
final String email;
|
||||
const CreateNewPw({Key? key, required this.userId, required this.email});
|
||||
final String? keyParam;
|
||||
|
||||
const CreateNewPw({Key? key, required this.userId, required this.email, required this.keyParam});
|
||||
|
||||
@override
|
||||
State<CreateNewPw> createState() => _CreateNewPwState();
|
||||
@ -210,7 +212,14 @@ class _CreateNewPwState extends State<CreateNewPw> {
|
||||
alignment: Alignment.topRight,
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
context.go('/editProfile');
|
||||
|
||||
if (widget.keyParam == 'editProfile') {
|
||||
context.go('/editProfile');
|
||||
}else
|
||||
{
|
||||
context.go('/profile/${widget.userId}');
|
||||
}
|
||||
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.only(top: 16,left: 16,bottom: 16,right: 1), // Add margin for positioning
|
||||
@ -351,8 +360,8 @@ class _CreateNewPwState extends State<CreateNewPw> {
|
||||
SizedBox(height: screenHeight / 5),
|
||||
Center(
|
||||
child: Container(
|
||||
height: screenHeight / 8,
|
||||
width: screenWidth / 2,
|
||||
height: screenHeight / 12,
|
||||
width: screenWidth / 2.5,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage("assets/splash_screen/logo.png"),
|
||||
|
||||
@ -31,6 +31,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
String? _password;
|
||||
bool registrationSuccess = false;
|
||||
bool registrationFailed = false;
|
||||
bool isRegistering = false;
|
||||
dynamic userID;
|
||||
|
||||
final pb = PocketBase('https://pb.venbait.in');
|
||||
@ -144,6 +145,12 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
final regex = RegExp(
|
||||
r'^[A-Za-z0-9~!@#$%^&*()_+=[\]{}|;:,.<>?/-àèìòùáéíóúÀÈÌÒÙÁÉÍÓÚ]*$');
|
||||
|
||||
// Define regular expressions for password complexity requirements
|
||||
final hasUppercase = RegExp(r'[A-Z]');
|
||||
final hasLowercase = RegExp(r'[a-z]');
|
||||
final hasDigit = RegExp(r'\d');
|
||||
final hasSpecialCharacter = RegExp(r'[~!@#$%^&*()_+=[\]{}|;:,.<>?/-]');
|
||||
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Required';
|
||||
} else if (value.length < 8) {
|
||||
@ -151,7 +158,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
}
|
||||
|
||||
// Check the length constraint
|
||||
if (value.length < 8 || value.length > 40) {
|
||||
if (value.length < 8 || value.length > 64) {
|
||||
return 'Password must be between 8 and 64 characters';
|
||||
}
|
||||
|
||||
@ -160,6 +167,27 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
return 'Password contains invalid characters';
|
||||
}
|
||||
|
||||
// Track missing constraints
|
||||
List<String> missingConstraints = [];
|
||||
|
||||
if (!hasUppercase.hasMatch(value)) {
|
||||
missingConstraints.add('uppercase letter');
|
||||
}
|
||||
if (!hasLowercase.hasMatch(value)) {
|
||||
missingConstraints.add('lowercase letter');
|
||||
}
|
||||
if (!hasDigit.hasMatch(value)) {
|
||||
missingConstraints.add('numeric digit');
|
||||
}
|
||||
if (!hasSpecialCharacter.hasMatch(value)) {
|
||||
missingConstraints.add('special character');
|
||||
}
|
||||
|
||||
// If there are missing constraints, return a consolidated message
|
||||
if (missingConstraints.isNotEmpty) {
|
||||
return 'At least one ${missingConstraints.join(', ')}';
|
||||
}
|
||||
|
||||
_password = value; // Store the password for confirm password validation
|
||||
return null;
|
||||
}
|
||||
@ -185,12 +213,15 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
Future<void> _registerUser() async {
|
||||
if (_formKey.currentState?.validate() ?? false) {
|
||||
if (isChecked) {
|
||||
setState(() {
|
||||
isRegistering = true; // Disable the button
|
||||
});
|
||||
try {
|
||||
final adminAuth = await pb.admins
|
||||
.authWithPassword('pb@venbainfotech.com', 'pb@venbainfotech.com');
|
||||
|
||||
final adminToken = adminAuth.token;
|
||||
print('adminToken- ${adminToken}');
|
||||
print('adminTokenREgistration- ${adminToken}');
|
||||
// Create user in PocketBase
|
||||
final response = await pb.collection('users').create(body: {
|
||||
'uname': _usernameController.text,
|
||||
@ -211,6 +242,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
userID = response.id;
|
||||
registrationSuccess = true;
|
||||
registrationFailed = false; // Show success message on success
|
||||
isRegistering = false;
|
||||
});
|
||||
|
||||
// Navigate to ProfileScreen after successful registration
|
||||
@ -249,6 +281,9 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
registrationFailed = true;
|
||||
registrationSuccess = false;
|
||||
});
|
||||
setState(() {
|
||||
isRegistering = false; // Re-enable the button after error
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -330,7 +365,9 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
buildIconContainer(Icons.report, Color(0xFF7DAFBC)),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
"Your registration is pending for verification.",
|
||||
context.translate(
|
||||
'Your registration is pending for verification.',
|
||||
'تسجيلك في انتظار التحقق.'),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
@ -339,7 +376,9 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
"Kindly verify your mail to proceed further.",
|
||||
context.translate(
|
||||
'Kindly verify your mail to proceed further.',
|
||||
'يرجى التحقق من البريد الخاص بك للمضي قدما.'),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
@ -350,14 +389,14 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
ElevatedButton(
|
||||
onPressed: () => {context.go('/')},
|
||||
child: Text(
|
||||
'Go to Login',
|
||||
context.translate('Go to Login', 'اذهب إلى تسجيل الدخول'),
|
||||
),
|
||||
),
|
||||
SizedBox(height: screenheight / 5),
|
||||
Center(
|
||||
child: Container(
|
||||
height: screenheight / 8,
|
||||
width: screenwidth / 2,
|
||||
height: screenheight / 16,
|
||||
width: screenwidth / 2.5,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(
|
||||
@ -379,7 +418,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
buildIconContainer(Icons.report, Colors.red),
|
||||
SizedBox(height: 20),
|
||||
Text(
|
||||
"Sorry ${_usernameController.text}!",
|
||||
context.translate('Sorry ${_usernameController.text}!',
|
||||
'آسف ${_usernameController.text}!'),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
@ -388,7 +428,9 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
Text(
|
||||
"Your registration process failed. For further assistance, please contact support.",
|
||||
context.translate(
|
||||
'Your registration process failed. For further assistance, please contact support.',
|
||||
'فشلت عملية التسجيل الخاصة بك. لمزيد من المساعدة، يرجى الاتصال بالدعم.'),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
@ -409,14 +451,14 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
})
|
||||
},
|
||||
child: Text(
|
||||
'Retry',
|
||||
context.translate('Retry', 'أعد المحاولة'),
|
||||
),
|
||||
),
|
||||
SizedBox(height: screenheight / 5),
|
||||
Center(
|
||||
child: Container(
|
||||
height: screenheight / 8,
|
||||
width: screenwidth / 2,
|
||||
height: screenheight / 16,
|
||||
width: screenwidth / 2.5,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(
|
||||
@ -475,7 +517,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
decoration: InputDecoration(
|
||||
// hintText: 'Enter your email',
|
||||
hintText: _showHints[1]
|
||||
? AppLocalizations.of(context)!.enter_your_email
|
||||
? AppLocalizations.of(context)!
|
||||
.enter_your_email
|
||||
: null,
|
||||
// _showHints[1] ? 'Enter your email' : null,
|
||||
prefixIcon: Icon(
|
||||
@ -500,7 +543,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
obscureText: _obscurePassword,
|
||||
decoration: InputDecoration(
|
||||
hintText: _showHints[2]
|
||||
? AppLocalizations.of(context)!.enter_your_password
|
||||
? AppLocalizations.of(context)!
|
||||
.enter_your_password
|
||||
: null,
|
||||
// _showHints[2] ? 'Enter your password' : null,
|
||||
prefixIcon: Icon(
|
||||
@ -510,8 +554,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscurePassword
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () {
|
||||
@ -545,8 +589,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_obscureConfirmPassword
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
? Icons.visibility_off
|
||||
: Icons.visibility,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () {
|
||||
@ -632,7 +676,7 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
SizedBox(
|
||||
width: screenwidth / 1.3,
|
||||
child: ElevatedButton(
|
||||
onPressed: _registerUser,
|
||||
onPressed: isRegistering ? null : _registerUser,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(
|
||||
0xFFA7887A), // Brownish color for Register
|
||||
@ -719,8 +763,8 @@ class _RegisterScreenState extends State<RegisterScreen> {
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
height: screenheight / 8,
|
||||
width: screenwidth / 2,
|
||||
height: screenheight / 16,
|
||||
width: screenwidth / 2.5,
|
||||
decoration: BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage(
|
||||
|
||||
@ -4,9 +4,11 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
|
||||
import 'package:uae_stat/domain/use_cases/language.dart';
|
||||
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/uae_numbers_asset_path.dart';
|
||||
import 'package:uae_stat/presentation/Screens/charts/services/api_service.dart';
|
||||
import 'package:uae_stat/presentation/Screens/charts/widgets/chart_widget.dart';
|
||||
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
|
||||
import 'package:uae_stat/presentation/components/my_toggle.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart';
|
||||
@ -761,8 +763,8 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
filterDataSet = filterData.entries
|
||||
.map((entry) => {'key': entry.key, 'value': entry.value})
|
||||
.toList();
|
||||
// print("filterDataf1:- $filterData");
|
||||
// print("filterDataf11:- $filterDataSet");
|
||||
print("filterDataf1:- $filterData");
|
||||
print("filterDataf11:- $filterDataSet");
|
||||
} else {
|
||||
filterDataSet = data['filterData'] ?? [];
|
||||
print('filterData not found!');
|
||||
@ -1081,53 +1083,139 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
),
|
||||
),
|
||||
// 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(originalTabChartsData);
|
||||
// cardData = List.from(originalTabCardData);
|
||||
// });
|
||||
// // Reset the selectedFiltersStorage to empty when clearing
|
||||
// selectedFiltersStorage.clear();
|
||||
// Navigator.pop(context);
|
||||
// },
|
||||
// child: Text(context.translate(
|
||||
// 'Clear',
|
||||
// 'واضح',
|
||||
// )),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: Colors.grey,
|
||||
// ),
|
||||
// ),
|
||||
// ElevatedButton(
|
||||
// onPressed: () {
|
||||
// print(
|
||||
// "Selected Filters before applying: $selectedFilters");
|
||||
// setState(() {
|
||||
// chartsData = originalTabChartsData;
|
||||
// cardData = originalTabCardData;
|
||||
// });
|
||||
//
|
||||
// applyFilters(context, filters, chartsData, cardData,
|
||||
// selectedFilters);
|
||||
// // Save the selected filters to storage after applying
|
||||
// selectedFiltersStorage = List.from(selectedFilters);
|
||||
// },
|
||||
// child: Text(context.translate(
|
||||
// 'Apply Filter',
|
||||
// 'تطبيق الفلتر',
|
||||
// )),
|
||||
// style: ElevatedButton.styleFrom(
|
||||
// backgroundColor: Colors.blue,
|
||||
// ),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
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(originalTabChartsData);
|
||||
cardData = List.from(originalTabCardData);
|
||||
});
|
||||
// Reset the selectedFiltersStorage to empty when clearing
|
||||
selectedFiltersStorage.clear();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Text(context.translate(
|
||||
'Clear',
|
||||
'واضح',
|
||||
)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.grey,
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
selectedFilters.forEach((filter) {
|
||||
filter["filter_data"].clear();
|
||||
});
|
||||
setState(() {
|
||||
chartsData = List.from(originalTabChartsData);
|
||||
cardData = List.from(originalTabCardData);
|
||||
});
|
||||
selectedFiltersStorage.clear();
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12), // Added padding
|
||||
child: Text(
|
||||
context.translate('Clear', 'واضح'),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
overflow:
|
||||
TextOverflow.ellipsis, // Prevents wrapping
|
||||
maxLines: 1, // Ensures single line
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF92722A),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
print(
|
||||
"Selected Filters before applying: $selectedFilters");
|
||||
setState(() {
|
||||
chartsData = originalTabChartsData;
|
||||
cardData = originalTabCardData;
|
||||
});
|
||||
SizedBox(width: 16),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () {
|
||||
print(
|
||||
"Selected Filters before applying: $selectedFilters");
|
||||
setState(() {
|
||||
chartsData = originalTabChartsData;
|
||||
cardData = originalTabCardData;
|
||||
});
|
||||
|
||||
applyFilters(context, filters, chartsData, cardData,
|
||||
selectedFilters);
|
||||
// Save the selected filters to storage after applying
|
||||
selectedFiltersStorage = List.from(selectedFilters);
|
||||
},
|
||||
child: Text(context.translate(
|
||||
'Apply Filter',
|
||||
'تطبيق الفلتر',
|
||||
)),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.blue,
|
||||
applyFilters(context, filters, chartsData, cardData,
|
||||
selectedFilters);
|
||||
selectedFiltersStorage = List.from(selectedFilters);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12), // Added padding
|
||||
child: FittedBox(
|
||||
fit: BoxFit
|
||||
.scaleDown, // Ensures text resizes if necessary
|
||||
child: Text(
|
||||
context.translate(
|
||||
'Apply Filter', 'تطبيق الفلتر'),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
overflow:
|
||||
TextOverflow.ellipsis, // Prevents wrapping
|
||||
maxLines: 1, // Ensures single line
|
||||
),
|
||||
),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Color(0xFF92722A),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -1235,9 +1323,18 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locale = ref.watch(localeProvider);
|
||||
final localeNotifier = ref.read(localeProvider.notifier);
|
||||
ref.listen<Locale?>(localeProvider, (previous, next) {
|
||||
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
|
||||
fetchChartData(widget.dataSets, localeCode);
|
||||
// fetchChartData(widget.dataSets, localeCode);
|
||||
|
||||
fetchChartData(widget.dataSets, localeCode).then((_) {
|
||||
if (_tabsData.isNotEmpty) {
|
||||
// Call onTabSelected for the first tab
|
||||
onTabSelected(_tabsData[0]['id']);
|
||||
}
|
||||
});
|
||||
});
|
||||
double myheight = MediaQuery.of(context).size.height;
|
||||
double mywidth = MediaQuery.of(context).size.width;
|
||||
@ -1249,6 +1346,23 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
appBar: AppBar(
|
||||
backgroundColor: color,
|
||||
elevation: 0,
|
||||
actions: [
|
||||
// IconButton(
|
||||
// onPressed: () {},
|
||||
// icon: const Icon(Icons.toggle_off_outlined),
|
||||
// ),
|
||||
MyToggle(
|
||||
// key: toggleKey,
|
||||
isOn: locale?.languageCode == 'en',
|
||||
knobTextWhenOn: 'ع',
|
||||
knobTextWhenOff: 'EN',
|
||||
pathColorWhenOn: Colors.grey.shade300,
|
||||
pathColorWhenOff: Colors.grey.shade300,
|
||||
onTap: () {
|
||||
ref.read(localeProvider.notifier).toggleLocale();
|
||||
},
|
||||
),
|
||||
],
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back_ios_new, color: Colors.white),
|
||||
onPressed: () {
|
||||
@ -1343,8 +1457,12 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Icon(Icons.bookmarks_outlined,
|
||||
color: Colors.white, size: 18),
|
||||
Image.asset(
|
||||
UaeNumbersAssetPath.bookmarksUae,
|
||||
color: Colors.white,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 10),
|
||||
@ -1361,8 +1479,12 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
),
|
||||
),
|
||||
SizedBox(width: 5),
|
||||
Icon(Icons.share_sharp,
|
||||
color: Colors.white, size: 18),
|
||||
Image.asset(
|
||||
UaeNumbersAssetPath.shareUae,
|
||||
color: Colors.white,
|
||||
width: 24,
|
||||
height: 24,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
@ -1492,10 +1614,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
child: FittedBox(
|
||||
// fit: BoxFit.contain,
|
||||
child: Text(
|
||||
'${chart_heading ?? 'NA'}',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2, // Limit to 2 lines
|
||||
softWrap:
|
||||
true, // Enable soft wrapping
|
||||
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
@ -1584,19 +1709,28 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(
|
||||
'${chart_heading ?? 'NA'}',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.black87,
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
child: FittedBox(
|
||||
child: Text(
|
||||
'${chart_heading ?? 'NA'}',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2, // Limit to 2 lines
|
||||
softWrap:
|
||||
true, // Enable soft wrapping
|
||||
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'(${data['firstYear'] ?? 'NA'} - ${data['lastYear'] ?? 'NA'})',
|
||||
style: const TextStyle(
|
||||
fontSize: 11, color: Colors.grey),
|
||||
fontSize: 10, color: Colors.grey),
|
||||
),
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
@ -1640,10 +1774,13 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
child: FittedBox(
|
||||
// fit: BoxFit.contain,
|
||||
child: Text(
|
||||
'${chart_heading ?? 'NA'}',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2, // Limit to 2 lines
|
||||
softWrap:
|
||||
true, // Enable soft wrapping
|
||||
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
@ -1715,14 +1852,17 @@ class _ChartScreen1State extends ConsumerState<ChartScreen1> {
|
||||
Flexible(
|
||||
fit: FlexFit.loose,
|
||||
child: FittedBox(
|
||||
fit: BoxFit.contain,
|
||||
child: Text(
|
||||
apiService.formatAmount(
|
||||
data['lastYearValue']),
|
||||
style: const TextStyle(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w900,
|
||||
color: Color(0xFF90B0D5),
|
||||
'${chart_heading ?? 'NA'}',
|
||||
textAlign: TextAlign.center,
|
||||
maxLines: 2, // Limit to 2 lines
|
||||
softWrap:
|
||||
true, // Enable soft wrapping
|
||||
// overflow: TextOverflow.ellipsis, // Handle overflow gracefully
|
||||
style: TextStyle(
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w400,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@ -72,27 +72,34 @@ class ChartWidget extends StatelessWidget {
|
||||
}).toList();
|
||||
}
|
||||
|
||||
List<Widget> generateIndicators(dynamic chartData, groupByValue) {
|
||||
List<Widget> generateIndicators(dynamic chartData, String groupByValue) {
|
||||
return chartData['response'].asMap().entries.map<Widget>((entry) {
|
||||
int index = entry.key;
|
||||
var data = entry.value;
|
||||
Color color = Colors.primaries[index % Colors.primaries.length];
|
||||
String title = data['ObsKey'][groupByValue] ?? '';
|
||||
String shortTitle =
|
||||
title.length > 10 ? '${title.substring(0, 10)}…' : title;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
width: 10,
|
||||
height: 10,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
SizedBox(width: 8),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(fontSize: 14),
|
||||
Tooltip(
|
||||
message: title, // Full text on hover
|
||||
child: Text(
|
||||
shortTitle,
|
||||
style: TextStyle(fontSize: 12),
|
||||
overflow: TextOverflow.ellipsis, // Ensures text doesn't overflow
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -299,7 +306,7 @@ class ChartWidget extends StatelessWidget {
|
||||
touchTooltipData: BarTouchTooltipData(
|
||||
// tooltipBgColor: Colors.black.withOpacity(0.8),
|
||||
fitInsideHorizontally: true,
|
||||
fitInsideVertically: false,
|
||||
fitInsideVertically: true,
|
||||
tooltipPadding: const EdgeInsets.all(8),
|
||||
tooltipMargin: 16,
|
||||
getTooltipItem:
|
||||
@ -541,11 +548,21 @@ class ChartWidget extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
]);
|
||||
case 'line_trend_2':
|
||||
case 'line_trend_population':
|
||||
final List<Color> uniqueColorsLine_trend_2 = [
|
||||
Color(0xFF6097CD),
|
||||
Color(0xFFD086A7),
|
||||
Color(0xFF98BCE5),
|
||||
Color(0xFFA7B5C5),
|
||||
Color(0xFFBED3EC),
|
||||
Color(0xFFD4E3F4),
|
||||
];
|
||||
Set<int> selectedYears = {1970, 1980, 1990, 2000, 2010, 2020};
|
||||
Map<String, Color> groupColorMap = {};
|
||||
int colorIndex = 0;
|
||||
for (String group in groupByValues) {
|
||||
groupColorMap[group] = uniqueColors[colorIndex % uniqueColors.length];
|
||||
groupColorMap[group] = uniqueColorsLine_trend_2[
|
||||
colorIndex % uniqueColorsLine_trend_2.length];
|
||||
colorIndex++;
|
||||
}
|
||||
|
||||
@ -565,15 +582,17 @@ class ChartWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// Find the maximum year and calculate the range for the last 5 years
|
||||
int maxYear = years.reduce((a, b) => a > b ? a : b);
|
||||
int minYear = maxYear - 5;
|
||||
if (years.isNotEmpty) {
|
||||
// Find the latest available year
|
||||
int latestYear = years.reduce((a, b) => a > b ? a : b);
|
||||
selectedYears.add(latestYear); // Include the latest year in selection
|
||||
}
|
||||
|
||||
// Filter chart data to only include entries within the last 5 years
|
||||
// Filter chart data to include only the selected years
|
||||
List<dynamic> filteredData =
|
||||
(chartData['response'] as List<dynamic>).where((entry) {
|
||||
int year = int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0;
|
||||
return year >= minYear && year <= maxYear;
|
||||
return selectedYears.contains(year);
|
||||
}).toList();
|
||||
|
||||
Set<double> uniqueXValues = filteredData
|
||||
@ -583,7 +602,7 @@ class ChartWidget extends StatelessWidget {
|
||||
|
||||
// Generate line bars for the chart
|
||||
List<LineChartBarData> lineBars =
|
||||
lineBarsData2(filteredData, groupByValues, groupByKey);
|
||||
lineBarsData(filteredData, groupByValues, groupByKey);
|
||||
|
||||
return Column(children: [
|
||||
Text(
|
||||
@ -600,12 +619,124 @@ class ChartWidget extends StatelessWidget {
|
||||
child: LineChart(LineChartData(
|
||||
lineTouchData: lineTouchData1(),
|
||||
gridData: gridData(),
|
||||
titlesData: titlesData2(uniqueXValues),
|
||||
titlesData: titlesData1(uniqueXValues),
|
||||
borderData: borderData(),
|
||||
lineBarsData: lineBars,
|
||||
minX: uniqueXValues.reduce((a, b) => a < b ? a : b),
|
||||
maxX: uniqueXValues.reduce((a, b) => a > b ? a : b),
|
||||
))),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Wrap(
|
||||
spacing: 12,
|
||||
runSpacing: 8,
|
||||
children: groupByValues.map((group) {
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 12,
|
||||
height: 12,
|
||||
color: groupColorMap[group],
|
||||
),
|
||||
SizedBox(width: 6),
|
||||
Text(
|
||||
group,
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
],
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
]);
|
||||
case 'line_trend_2':
|
||||
final List<Color> uniqueColorsLine_trend_2 = [
|
||||
Color(0xFF6097CD),
|
||||
Color(0xFFD086A7),
|
||||
Color(0xFF98BCE5),
|
||||
Color(0xFFA7B5C5),
|
||||
Color(0xFFBED3EC),
|
||||
Color(0xFFD4E3F4),
|
||||
];
|
||||
Set<int> selectedYears = {1970, 1980, 1990, 2000, 2010, 2020};
|
||||
Map<String, Color> groupColorMap = {};
|
||||
int colorIndex = 0;
|
||||
for (String group in groupByValues) {
|
||||
groupColorMap[group] = uniqueColorsLine_trend_2[
|
||||
colorIndex % uniqueColorsLine_trend_2.length];
|
||||
colorIndex++;
|
||||
}
|
||||
|
||||
// Extract all years from the chart data
|
||||
List<int> years = (chartData['response'] as List<dynamic>)
|
||||
.map<int>((entry) =>
|
||||
int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0)
|
||||
.toList();
|
||||
|
||||
if (years.isEmpty) {
|
||||
// Return an empty chart if no data
|
||||
return LineChart(
|
||||
LineChartData(
|
||||
titlesData: FlTitlesData(show: false),
|
||||
lineBarsData: [],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if (years.isNotEmpty) {
|
||||
// Find the latest available year
|
||||
int latestYear = years.reduce((a, b) => a > b ? a : b);
|
||||
selectedYears.add(latestYear); // Include the latest year in selection
|
||||
}
|
||||
|
||||
// Filter chart data to include only the selected years
|
||||
List<dynamic> filteredData =
|
||||
(chartData['response'] as List<dynamic>).where((entry) {
|
||||
int year = int.tryParse(entry['ObsKey']['TIME_PERIOD'] ?? '0') ?? 0;
|
||||
return selectedYears.contains(year);
|
||||
}).toList();
|
||||
|
||||
Set<double> uniqueXValues = filteredData
|
||||
.map<double>(
|
||||
(entry) => double.parse(entry['ObsKey']['TIME_PERIOD']))
|
||||
.toSet();
|
||||
|
||||
// Generate line bars for the chart
|
||||
List<LineChartBarData> lineBars = lineBarsData2(filteredData);
|
||||
|
||||
return Column(children: [
|
||||
Text(
|
||||
chartData['chart_heading'] ?? '', // Chart title from data
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
// Chart
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal, // Enable horizontal scrolling
|
||||
padding: const EdgeInsets.only(right: 40),
|
||||
child: SizedBox(
|
||||
width: (uniqueXValues.length * 80) +
|
||||
80, // Adjust width dynamically
|
||||
child: LineChart(
|
||||
LineChartData(
|
||||
lineTouchData: lineTouchData1(),
|
||||
gridData: gridData(),
|
||||
titlesData: titlesData1(uniqueXValues),
|
||||
borderData: borderData(),
|
||||
lineBarsData: lineBars,
|
||||
minX: uniqueXValues.reduce((a, b) => a < b ? a : b),
|
||||
maxX: uniqueXValues.reduce((a, b) => a > b ? a : b),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
]);
|
||||
case 'bar_chart':
|
||||
// Extract groupBy values and their corresponding y-axis values
|
||||
@ -697,6 +828,106 @@ class ChartWidget extends StatelessWidget {
|
||||
),
|
||||
))
|
||||
]);
|
||||
case 'bar_chart_horizontal':
|
||||
// Extract groupBy values and their corresponding y-axis values
|
||||
List<String> xAxisData = [];
|
||||
List<double> yAxisData = [];
|
||||
|
||||
for (var entry in chartData['response']) {
|
||||
var xValue = entry['ObsKey'][groupByKey];
|
||||
var yValue = entry['ObsValue']['Value'];
|
||||
if (xValue != null && yValue != null) {
|
||||
xAxisData.add(xValue.toString());
|
||||
yAxisData.add(double.tryParse(yValue.toString()) ?? 0.0);
|
||||
}
|
||||
}
|
||||
int rotationTurns = 1;
|
||||
return Column(children: [
|
||||
Text(
|
||||
chartData['chart_heading'] ?? '', // Chart title from data
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 10),
|
||||
// Chart
|
||||
Expanded(
|
||||
child: BarChart(
|
||||
BarChartData(
|
||||
alignment: BarChartAlignment.spaceAround,
|
||||
maxY: yAxisData.isNotEmpty
|
||||
? yAxisData.reduce((a, b) => a > b ? a : b) * 1.2
|
||||
: 10,
|
||||
rotationQuarterTurns: rotationTurns,
|
||||
barTouchData: BarTouchData(enabled: true),
|
||||
titlesData: FlTitlesData(
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
interval: (yAxisData.isNotEmpty
|
||||
? yAxisData.reduce((a, b) => a > b ? a : b) / 5
|
||||
: 1),
|
||||
getTitlesWidget: (value, meta) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(right: 8.0),
|
||||
child: Text('${value.toInt()}'),
|
||||
);
|
||||
},
|
||||
reservedSize: 60,
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
getTitlesWidget: (value, meta) {
|
||||
if (value.toInt() < xAxisData.length) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 8.0),
|
||||
child: Transform.rotate(
|
||||
angle: -45 *
|
||||
(3.1415927 / 180), // Rotating by -45 degrees
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
xAxisData[value.toInt()],
|
||||
style: TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
},
|
||||
reservedSize: 60,
|
||||
),
|
||||
),
|
||||
topTitles: AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false), // Hide top titles
|
||||
),
|
||||
rightTitles: AxisTitles(
|
||||
sideTitles:
|
||||
SideTitles(showTitles: false), // Hide right titles
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(show: false),
|
||||
borderData: FlBorderData(show: false),
|
||||
barGroups: List.generate(
|
||||
xAxisData.length,
|
||||
(index) => BarChartGroupData(
|
||||
x: index,
|
||||
barRods: [
|
||||
BarChartRodData(
|
||||
toY: yAxisData[index],
|
||||
color: Colors.blueAccent,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
width: 20,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
))
|
||||
]);
|
||||
case 'fl_multi_bar':
|
||||
double _calculateChartWidth(dynamic chartData) {
|
||||
int totalBars = chartData['response']?.length ?? 0;
|
||||
@ -712,6 +943,15 @@ class ChartWidget extends StatelessWidget {
|
||||
if (chartData['dataset'] == 'health_services') {
|
||||
crop = item['ObsKey']['SECTOR'];
|
||||
cropType = item['ObsKey'][groupByKey];
|
||||
} else if (chartData['dataset'] == 'general_education') {
|
||||
crop = item['ObsKey']['GENDER'];
|
||||
cropType = item['ObsKey'][groupByKey];
|
||||
} else if (chartData['dataset'] == 'higher_education') {
|
||||
crop = item['ObsKey']['GENDER'];
|
||||
cropType = item['ObsKey'][groupByKey];
|
||||
} else if (chartData['dataset'] == 'labour_force') {
|
||||
crop = item['ObsKey']['GENDER'];
|
||||
cropType = item['ObsKey'][groupByKey];
|
||||
} else {
|
||||
crop = item['ObsKey']['CROP'];
|
||||
cropType = item['ObsKey'][groupByKey];
|
||||
@ -781,6 +1021,12 @@ class ChartWidget extends StatelessWidget {
|
||||
barTouchData: BarTouchData(
|
||||
touchTooltipData: BarTouchTooltipData(
|
||||
tooltipHorizontalAlignment: FLHorizontalAlignment.center,
|
||||
tooltipRoundedRadius: 8,
|
||||
fitInsideHorizontally:
|
||||
true, // Ensure it fits within the screen
|
||||
fitInsideVertically: true,
|
||||
tooltipPadding: EdgeInsets.all(8),
|
||||
tooltipMargin: 16,
|
||||
// Only show tooltip when touched
|
||||
getTooltipItem: (group, groupIndex, rod, rodIndex) {
|
||||
if (rod.toY == 0 || touchedGroupIndex == -1) {
|
||||
@ -873,8 +1119,14 @@ class ChartWidget extends StatelessWidget {
|
||||
|
||||
// Iterate over the chartData to group crops by CROP_TYPE
|
||||
for (var item in chartData['response']) {
|
||||
String crop = item['ObsKey']['CROP'];
|
||||
String cropType = item['ObsKey']['CROP_TYPE'];
|
||||
String crop, cropType;
|
||||
if (chartData['dataset'] == 'natural_reserves') {
|
||||
crop = item['ObsKey']['NR_TYPE'];
|
||||
cropType = item['ObsKey'][groupByKey];
|
||||
} else {
|
||||
crop = item['ObsKey']['CROP'];
|
||||
cropType = item['ObsKey'][groupByKey];
|
||||
}
|
||||
|
||||
if (groupedCrops.containsKey(cropType)) {
|
||||
groupedCrops[cropType]!.add(crop);
|
||||
@ -985,15 +1237,22 @@ class ChartWidget extends StatelessWidget {
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
reservedSize: 20, // Added space for rotated titles
|
||||
reservedSize: 80, // Added space for rotated titles
|
||||
getTitlesWidget: (value, meta) {
|
||||
if (value < groupByValues.length) {
|
||||
String title = groupByValues.elementAt(value.toInt());
|
||||
return Transform.rotate(
|
||||
angle: -0.5, // Rotation in radians (~ -30 degrees)
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
angle: -1.58, // Rotation in radians (~ -30 degrees)
|
||||
child: Center(
|
||||
child: SizedBox(
|
||||
width: 80,
|
||||
child: Text(
|
||||
title,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
softWrap: true,
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -1010,7 +1269,7 @@ class ChartWidget extends StatelessWidget {
|
||||
show: true,
|
||||
border: const Border(
|
||||
// left: BorderSide(color: Colors.grey),
|
||||
bottom: BorderSide(color: Colors.grey),
|
||||
bottom: BorderSide(color: Colors.white),
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(
|
||||
@ -1084,47 +1343,25 @@ class ChartWidget extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
List<LineChartBarData> lineBarsData2(List<dynamic> filteredData,
|
||||
Set<String> groupByValues, String groupByKey) {
|
||||
List<LineChartBarData> lineBarsData2(List<dynamic> filteredData) {
|
||||
List<LineChartBarData> lineBars = [];
|
||||
print('filteredData $filteredData');
|
||||
print('filteredData222 $groupByValues');
|
||||
final List<Color> uniqueColors = [
|
||||
Color(0xFF648CBA),
|
||||
Color(0xFF90B0D5),
|
||||
Color(0xFF98BCE5),
|
||||
Color(0xFFA7B5C5),
|
||||
Color(0xFFBED3EC),
|
||||
Color(0xFFD4E3F4),
|
||||
];
|
||||
|
||||
// Create a map to assign colors to each group in order
|
||||
Map<String, Color> groupColorMap = {};
|
||||
int colorIndex = 0;
|
||||
for (String group in groupByValues) {
|
||||
groupColorMap[group] = uniqueColors[colorIndex % uniqueColors.length];
|
||||
colorIndex++;
|
||||
}
|
||||
|
||||
print('Group-Color Map: $groupColorMap');
|
||||
|
||||
// Create a map to store the values by year and gender (M and F)
|
||||
// Create a map to store population values by year and gender
|
||||
Map<int, Map<String, double>> yearGenderMap = {};
|
||||
|
||||
// Populate the yearGenderMap with male and female values
|
||||
for (var entry in filteredData) {
|
||||
int year = int.parse(entry['ObsKey']['TIME_PERIOD']);
|
||||
int year = int.tryParse(entry['ObsKey']['TIME_PERIOD'].toString()) ?? 0;
|
||||
String gender = entry['ObsKey']['GENDER'];
|
||||
double value = double.parse(entry['ObsValue']['Value'].toString());
|
||||
double value =
|
||||
double.tryParse(entry['ObsValue']['Value'].toString()) ?? 0.0;
|
||||
|
||||
if (!yearGenderMap.containsKey(year)) {
|
||||
yearGenderMap[year] = {'M': 0.0, 'F': 0.0};
|
||||
}
|
||||
|
||||
// Assign value based on gender
|
||||
if (gender == 'M') {
|
||||
if (gender == 'Male') {
|
||||
yearGenderMap[year]!['M'] = value;
|
||||
} else if (gender == 'F') {
|
||||
} else if (gender == 'Female') {
|
||||
yearGenderMap[year]!['F'] = value;
|
||||
}
|
||||
}
|
||||
@ -1205,7 +1442,14 @@ class ChartWidget extends StatelessWidget {
|
||||
// Helper functions for chart styles
|
||||
LineTouchData lineTouchData1() {
|
||||
return LineTouchData(
|
||||
touchTooltipData: LineTouchTooltipData(),
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
// tooltipBgColor: Colors.black.withOpacity(0.7), // Tooltip background
|
||||
tooltipRoundedRadius: 8,
|
||||
fitInsideHorizontally: true, // Ensure it fits within the screen
|
||||
fitInsideVertically: true,
|
||||
tooltipPadding: EdgeInsets.all(8),
|
||||
tooltipMargin: 16, // Adds margin to prevent clipping
|
||||
),
|
||||
handleBuiltInTouches: true,
|
||||
);
|
||||
}
|
||||
@ -1534,6 +1778,12 @@ class ChartWidget extends StatelessWidget {
|
||||
toY: value, // Use the parsed value
|
||||
color: barColor, // Dynamic color
|
||||
width: 20,
|
||||
// backDrawRodData: BackgroundBarChartRodData(
|
||||
// show: true,
|
||||
// toY: 400000,
|
||||
// color: Colors.grey.shade300,
|
||||
// ),
|
||||
//
|
||||
);
|
||||
}).toList();
|
||||
|
||||
|
||||
@ -451,7 +451,7 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
controller: _usernameController,
|
||||
focusNode: _focusNodes[0],
|
||||
decoration: InputDecoration(
|
||||
hintText: _showHints[0] ? 'Enter the User Name' : null,
|
||||
// hintText: _showHints[0] ? 'Enter the User Name' : null,
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
@ -477,8 +477,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
|
||||
controller: _emailController,
|
||||
focusNode: _focusNodes[1],
|
||||
decoration: InputDecoration(
|
||||
hintText:
|
||||
_showHints[1] ? 'mohammad.hassan@fcsc.gov.ae' : null,
|
||||
// hintText:
|
||||
// _showHints[1] ? 'mohammad.hassan@fcsc.gov.ae' : null,
|
||||
hintStyle: TextStyle(color: Colors.grey),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
|
||||
@ -354,14 +354,22 @@ class LoginRoute extends HookConsumerWidget {
|
||||
'Email',
|
||||
'بريد إلكتروني',
|
||||
),
|
||||
validator: FieldValidator.email(),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return context.translate('Required', 'مطلوب');
|
||||
}
|
||||
return FieldValidator.email()(value);
|
||||
},
|
||||
imgPath: MiscIconAssetPath.person,
|
||||
controller: emailCtl,
|
||||
),
|
||||
15.verticalSpace,
|
||||
ThemedFormField(
|
||||
validator: (text) {
|
||||
if (text!.length < 8) {
|
||||
if (text == null || text.isEmpty) {
|
||||
return context.translate('Required', 'مطلوب');
|
||||
}
|
||||
else if (text.length < 8) {
|
||||
return 'The password must be at least 8 characters';
|
||||
}
|
||||
return FieldValidator.password(minLength: 8)(text);
|
||||
@ -462,16 +470,16 @@ class LoginRoute extends HookConsumerWidget {
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: () async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.clear();
|
||||
final userId = 'guest';
|
||||
if (userId.isNotEmpty) {
|
||||
await saveUserId(userId);
|
||||
}
|
||||
context.go('/myhomepage');
|
||||
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
|
||||
},
|
||||
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
prefs.clear();
|
||||
final userId = 'guest';
|
||||
if (userId.isNotEmpty) {
|
||||
await saveUserId(userId);
|
||||
}
|
||||
context.go('/myhomepage');
|
||||
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
|
||||
},
|
||||
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
|
||||
style: ButtonStyle(
|
||||
shape: WidgetStatePropertyAll(
|
||||
RoundedRectangleBorder(
|
||||
@ -515,7 +523,7 @@ class LoginRoute extends HookConsumerWidget {
|
||||
);
|
||||
final fcscBanner = Image.asset(
|
||||
BannerAssetPath.fcsc,
|
||||
height: 56,
|
||||
height: 40,
|
||||
);
|
||||
final screenWidth = MediaQuery.of(context).size.width;
|
||||
final listViewHorizontalPadding =
|
||||
@ -528,14 +536,16 @@ class LoginRoute extends HookConsumerWidget {
|
||||
36.verticalSpace,
|
||||
Align(
|
||||
alignment: AlignmentDirectional.topEnd,
|
||||
child: MyToggle(isOn: locale?.languageCode == 'en',
|
||||
child: MyToggle(
|
||||
isOn: locale?.languageCode == 'en',
|
||||
knobTextWhenOn: 'ع',
|
||||
knobTextWhenOff: 'EN',
|
||||
pathColorWhenOn: Colors.grey.shade300,
|
||||
pathColorWhenOff: Colors.grey.shade300,
|
||||
onTap: (){
|
||||
onTap: () {
|
||||
ref.read(localeProvider.notifier).toggleLocale();
|
||||
},),
|
||||
},
|
||||
),
|
||||
),
|
||||
16.verticalSpace,
|
||||
helloAndPleaseLoginTexts,
|
||||
@ -543,9 +553,9 @@ class LoginRoute extends HookConsumerWidget {
|
||||
form,
|
||||
20.verticalSpace,
|
||||
dontHaveAnAccountRegisterBtn,
|
||||
36.verticalSpace,
|
||||
20.verticalSpace,
|
||||
continueAsGuestBtn,
|
||||
72.verticalSpace,
|
||||
25.verticalSpace,
|
||||
fcscBanner,
|
||||
],
|
||||
);
|
||||
|
||||
@ -753,14 +753,31 @@ class _BaseScaffoldState extends ConsumerState<BaseScaffold> {
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.book),
|
||||
title: const Text('User Guide'),
|
||||
// title: const Text('User Guide'),
|
||||
title: Text(AppLocalizations.of(context)!.guide_title),
|
||||
onTap: () => context.go('/user-guide'),
|
||||
),
|
||||
ListTile(
|
||||
leading: Icon(Icons.logout),
|
||||
title: const Text('Logout'),
|
||||
onTap: () => logout(),
|
||||
),
|
||||
if (userId != 'guest')
|
||||
ListTile(
|
||||
leading: Icon(Icons.logout),
|
||||
// title: const Text('Logout'),
|
||||
title: Text(AppLocalizations.of(context)!.logout),
|
||||
onTap: () => logout(),
|
||||
),
|
||||
if (userId == 'guest')
|
||||
ListTile(
|
||||
leading: Icon(Icons.login),
|
||||
// title: const Text('Login'),
|
||||
title: Text(AppLocalizations.of(context)!.login_title),
|
||||
onTap: () => context.go('/login'),
|
||||
),
|
||||
if (userId == 'guest')
|
||||
ListTile(
|
||||
leading: Icon(Icons.app_registration),
|
||||
// title: const Text('Register'),
|
||||
title: Text(AppLocalizations.of(context)!.register_title),
|
||||
onTap: () => context.go('/register'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -2,7 +2,7 @@ name: uae_stat
|
||||
description: "View statistics about the UAE from the Federal Center for Statistics and Competitiveness."
|
||||
publish_to: "none"
|
||||
#version: 0.5.10
|
||||
version: 1.0.2+3
|
||||
version: 1.0.5+6
|
||||
|
||||
environment:
|
||||
sdk: ">=3.2.3 <4.0.0"
|
||||
@ -115,6 +115,8 @@ flutter:
|
||||
- assets/edit_profile/
|
||||
- assets/splash_screen/
|
||||
- assets/app_tour/
|
||||
- assets/icons/uae_numbers/bookmarks.png
|
||||
- assets/icons/uae_numbers/share.png
|
||||
|
||||
fonts:
|
||||
- family: Segoe
|
||||
|
||||