Registration Page UI Updates

This commit is contained in:
VINISTAN 2025-02-06 16:47:57 +05:30
commit 0b0f7ca788
53 changed files with 7836 additions and 2287 deletions

BIN
FCSC_UAE_Stats.jks Normal file

Binary file not shown.

View File

@ -15,22 +15,30 @@ if (localPropertiesFile.exists()) {
def flutterVersionCode = localProperties.getProperty("flutter.versionCode") def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) { if (flutterVersionCode == null) {
flutterVersionCode = "1" flutterVersionCode = "9"
} }
def flutterVersionName = localProperties.getProperty("flutter.versionName") def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) { if (flutterVersionName == null) {
flutterVersionName = "1.0" flutterVersionName = "1.0.8"
} }
def keystorePropertiesFile = rootProject.file("key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android { android {
namespace = "ae.gov.fcsc.frontend" namespace = "ae.gov.fcsc.frontend"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
// ndkVersion = flutter.ndkVersion // ndkVersion = flutter.ndkVersion
ndkVersion = "25.1.8937393" ndkVersion = "25.1.8937393"
compileOptions { compileOptions {
sourceCompatibility = JavaVersion.VERSION_21 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
} }
defaultConfig { defaultConfig {
@ -44,11 +52,22 @@ android {
versionName = flutterVersionName versionName = flutterVersionName
} }
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes { buildTypes {
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.debug signingConfig signingConfigs.release
minifyEnabled true // Enable code shrinking for smaller APKs
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
} }

View File

@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=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

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
assets/app_tour/leftUp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 B

BIN
assets/icons/misc/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 B

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -300,6 +300,7 @@ import 'package:uae_stat/presentation/Screens/charts/chart.dart';
import 'package:uae_stat/presentation/Screens/charts/screens/chart_screen.dart'; import 'package:uae_stat/presentation/Screens/charts/screens/chart_screen.dart';
import 'package:uae_stat/presentation/Screens/demo_home2.dart'; import 'package:uae_stat/presentation/Screens/demo_home2.dart';
import 'package:uae_stat/presentation/Screens/online_offline_verification/internet_check.dart'; import 'package:uae_stat/presentation/Screens/online_offline_verification/internet_check.dart';
import 'package:uae_stat/presentation/routes/auth_routes/pocketbase_service.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/getting_started.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/getting_started.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
@ -316,6 +317,7 @@ import '../presentation/Screens/demo_home.dart';
import '../presentation/Screens/profilepage.dart'; import '../presentation/Screens/profilepage.dart';
import '../presentation/routes/auth_routes/login_route.dart'; import '../presentation/routes/auth_routes/login_route.dart';
import '../presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; import '../presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/bookmark.dart';
import '../presentation/routes/drawer_routes/Drawer Items/edit_profile.dart'; import '../presentation/routes/drawer_routes/Drawer Items/edit_profile.dart';
import '../presentation/routes/drawer_routes/Drawer Items/feedback.dart'; import '../presentation/routes/drawer_routes/Drawer Items/feedback.dart';
import '../presentation/routes/drawer_routes/Drawer Items/user_guide/faq_page.dart'; import '../presentation/routes/drawer_routes/Drawer Items/user_guide/faq_page.dart';
@ -323,17 +325,29 @@ import '../presentation/routes/drawer_routes/Drawer Items/user_guide/features.da
import '../presentation/routes/drawer_routes/Drawer Items/user_guide/user_guide.dart'; import '../presentation/routes/drawer_routes/Drawer Items/user_guide/user_guide.dart';
import '../presentation/routes/drawer_routes/Drawer Items/manage_users.dart'; import '../presentation/routes/drawer_routes/Drawer Items/manage_users.dart';
final pbService = PocketBaseService();
Future<String> getInitialRoute() async {
bool isLoggedIn = await pbService.loadSession();
return isLoggedIn ? '/myhomepage' : '/login';
}
final GoRouter router = GoRouter( final GoRouter router = GoRouter(
routes: [ routes: [
GoRoute( GoRoute(
path: '/', path: '/',
//builder: (context, state) => LoginRoute(), //builder: (context, state) => LoginRoute(),
builder: (context, state) => MyHomePage(), builder: (context, state) => RegisterScreen(),
), ),
// GoRoute(
// path: '/',
// redirect: (context, state) async {
// return await getInitialRoute();
// },
// ),
GoRoute( GoRoute(
path: '/internetcheck', path: '/internetcheck',
builder: (context, state) => InternetCheck(), builder: (context, state) => InternetCheck(),
), ),
GoRoute( GoRoute(
path: '/login', path: '/login',
@ -374,6 +388,7 @@ final GoRouter router = GoRouter(
'0xFFFFFFFF'; // Default white '0xFFFFFFFF'; // Default white
final mainTopic = state.uri.queryParameters['mainTopic'] ?? ''; final mainTopic = state.uri.queryParameters['mainTopic'] ?? '';
final title = state.uri.queryParameters['title'] ?? ''; final title = state.uri.queryParameters['title'] ?? '';
final key = state.uri.queryParameters['key'] ?? '';
print('Router dataSets: $dataSets'); print('Router dataSets: $dataSets');
print('Router bgColor: $bgColor'); print('Router bgColor: $bgColor');
@ -386,6 +401,7 @@ final GoRouter router = GoRouter(
bgColor: bgColor, bgColor: bgColor,
mainTopic: mainTopic, mainTopic: mainTopic,
title: title, title: title,
keyParam: key,
); );
}, },
), ),
@ -411,7 +427,9 @@ final GoRouter router = GoRouter(
builder: (context, state) { builder: (context, state) {
final userId = state.pathParameters['userId']!; final userId = state.pathParameters['userId']!;
final email = state.pathParameters['email']!; 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( GoRoute(
@ -468,6 +486,10 @@ final GoRouter router = GoRouter(
path: '/editProfile', path: '/editProfile',
builder: (context, state) => EditProfile(), builder: (context, state) => EditProfile(),
), ),
GoRoute(
path: '/bookmark',
builder: (context, state) => BookMark(),
),
GoRoute( GoRoute(
path: '/manageuser', path: '/manageuser',
builder: (context, state) => ManageUserRouter(), builder: (context, state) => ManageUserRouter(),

View File

@ -63,36 +63,36 @@
// //
// //
import 'package:flutter_riverpod/flutter_riverpod.dart'; // import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:connectivity_plus/connectivity_plus.dart'; // import 'package:connectivity_plus/connectivity_plus.dart';
//
final connectivityProvider = StateNotifierProvider<ConnectivityNotifier, bool>((ref) { // final connectivityProvider = StateNotifierProvider<ConnectivityNotifier, bool>((ref) {
return ConnectivityNotifier(); // return ConnectivityNotifier();
}); // });
//
class ConnectivityNotifier extends StateNotifier<bool> { // class ConnectivityNotifier extends StateNotifier<bool> {
ConnectivityNotifier() : super(true) { // ConnectivityNotifier() : super(true) {
_checkInitialConnection(); // _checkInitialConnection();
_listenToConnectivityChanges(); // _listenToConnectivityChanges();
} // }
//
void _checkInitialConnection() async { // void _checkInitialConnection() async {
final result = await Connectivity().checkConnectivity(); // final result = await Connectivity().checkConnectivity();
state = _hasInternet(result); // state = _hasInternet(result);
} // }
//
void _listenToConnectivityChanges() { // void _listenToConnectivityChanges() {
// For mobile platforms, use the connectivity plugin // // For mobile platforms, use the connectivity plugin
//
// Listen for connectivity changes on web // // Listen for connectivity changes on web
Connectivity().onConnectivityChanged.listen((ConnectivityResult result) { // Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
state = _hasInternet(result); // state = _hasInternet(result);
}); // });
} // }
//
//
//
bool _hasInternet(ConnectivityResult result) { // bool _hasInternet(ConnectivityResult result) {
return result == ConnectivityResult.mobile || result == ConnectivityResult.wifi; // return result == ConnectivityResult.mobile || result == ConnectivityResult.wifi;
} // }
} // }

View File

@ -8,4 +8,7 @@ abstract class DrawerAssetIconPath {
static const message = '$_basePath/message.png'; static const message = '$_basePath/message.png';
static const scroll = '$_basePath/scroll.png'; static const scroll = '$_basePath/scroll.png';
static const manageuser = '$_basePath/manageuser.png'; static const manageuser = '$_basePath/manageuser.png';
static const manageUser = '$_basePath/manageusr.png';
static const guide = '$_basePath/guide.png';
static const feedback = '$_basePath/feedback.png';
} }

View File

@ -6,4 +6,9 @@ abstract class MiscIconAssetPath {
static const menu = '$_basePath/menu.png'; static const menu = '$_basePath/menu.png';
static const person = '$_basePath/person.png'; static const person = '$_basePath/person.png';
static const back = '$_basePath/back.png'; static const back = '$_basePath/back.png';
static const vector = '$_basePath/vector.png';
static const visibleOn = '$_basePath/visible_on.png';
static const visibilityOff = '$_basePath/visibility_off.png';
static const save = '$_basePath/save.png';
} }

View File

@ -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';
}

View File

@ -1,24 +1,24 @@
{ {
"feedback_title": "نموذج الملاحظات", "feedback_title": "التعليقات",
"feedback_failed_msg": "لم يتم إرسال تعليقاتك. يرجى المحاولة مرة أخرى", "feedback_failed_msg": "لم يتم إرسال تعليقاتك. يرجى المحاولة مرة أخرى.",
"guide_title": "نموذج الملاحظات", "guide_title": "نموذج الملاحظات",
"register_title": "التسجيل", "register_title": "التسجيل",
"register_details": "من فضلك ادخل بياناتك", "register_details": "من فضلك ادخل بياناتك",
"register_name": "اسم المستخدم", "register_name": "اسم المستخدم",
"email_id": "الاسم الكامل", "email_id": "معرف البريد الإلكتروني",
"enter_name": "أدخل الاسم", "enter_name": "أدخل الاسم",
"register_email": "أدخل بريدك الإلكتروني", "enter_your_email": "أدخل بريدك الإلكتروني",
"register_password": "دخل كلمة المرور", "enter_your_password": "أدخل كلمة المرور",
"register_Confirm_password": "أكيد كلمة المرو", "register_Confirm_password": "تأكيد كلمة المرور",
"agree": " وافق على", "agree": "أوافق على الشروط والأحكام وسياسة الخصوصية للمركز الإتحادي للتنافسية والإحصاء",
"t_and": "و ", "t_and": "",
"conditions": " الخاصة بالمركز الاتحادي للتنافسية والإحصاء", "conditions": "",
"terms_conditions": "الشروط والأحكام ", "terms_conditions": "الشروط والأحكام",
"privacy_policy":" وسياسة الخصوصية", "privacy_policy":"سياسة الخصوصية",
"account_confirmation": "ل لديك حساب؟ سجل الدخول ", "account_confirmation": "هل لديك حساب؟ سجل الدخول",
"login_title": "سجيل الدخول", "login_title": "تسجيل الدخول",
"profile_title": "نموذج الملاحظات", "profile_title": "نموذج الملاحظات",
"my_profile": "ملفي الشخصي", "my_profile": "ملفي الشخصي",
@ -30,8 +30,8 @@
"confirm": "تأكيد", "confirm": "تأكيد",
"change_password": "تغيير كلمة المرور", "change_password": "تغيير كلمة المرور",
"sure_save_page": " هل أنت متأكد أنك ترغب في حفظ هذه الصفحة؟", "sure_save_page": " هل أنت متأكد أنك ترغب في حفظ هذه الصفحة؟",
"not_able_to_change": " بمجرد الحفظ، لن تتمكن من تعديل اسمك أو تاريخ ", "not_able_to_change": "بمجرد الحفظ، لن تتمكن من تعديل اسمك أو تاريخ ميلادك.",
"save": "يحفظ", "save": "حفظ",
"name": "اسم", "name": "اسم",
@ -55,25 +55,25 @@
"password_match": "كلمات المرور غير متطابقة", "password_match": "كلمات المرور غير متطابقة",
"only8charac": "يجب أن تتكون كلمة المرور من 8 أحرف على الأقل", "only8charac": "يجب أن تتكون كلمة المرور من 8 أحرف على الأقل",
"manage_user": "دارة المستخدمين", "manage_user": "إدارة المستخدمين",
"user_name": "اسم المستخدم", "user_name": "اسم المستخدم",
"reg_date": "تاريخ التسجيل", "reg_date": "تاريخ التسجيل",
"status": "تمت الموافقة", "status": "الحالة",
"denied": "مرفوض", "denied": "مرفوض",
"pending": "يد الانتظار", "pending": " قيد الانتظار",
"approve_msg": "هل أنت متأكد أنك ترغب في تغيير الحالة إلى \"{newStatus}\"؟", "approve_msg": "هل أنت متأكد أنك ترغب في تغيير الحالة إلى \"{newStatus}\"؟",
"yes": "نعم", "yes": "نعم",
"no": "لا", "no": "لا",
"no_result_found": "لم يتم العثور على نتائج", "no_result_found": "لم يتم العثور على نتائج",
"search": "يبحث", "search": "يبحث",
"approved": "موافقة", "approved": "تمت الموافقة",
"create_new_password": "إنشاء كلمة مرور جديدة", "create_new_password": "إنشاء كلمة مرور جديدة",
"different_password": "يجب أن تكون كلمة المرور الجديدة مختلفة عن كلمة المرور المستخدمة سابقًا.", "different_password":"يجب أن تكون كلمة المرور الجديدة مختلفة عن كلمة المرور المستخدمة سابقًا.",
"old_password": "كلمة المرور القديمة", "old_password": "كلمة المرور القديمة",
"enter_new_password": "أدخل كلمة المرور الجديدة", "enter_new_password": "أدخل كلمة المرور الجديدة",
"confirm_new_password": "تأكيد كلمة المرور الجديدة", "confirm_new_password": "تأكيد كلمة المرور الجديدة",
"password_changed_successfully": "م تغيير كلمة المرور بنجاح", "password_changed_successfully": "تم تغيير كلمة المرور بنجاح",
"enter_old_password": "أدخل كلمة المرور القديمة", "enter_old_password": "أدخل كلمة المرور القديمة",
"your_old_password_incorrect": "كلمة المرور القديمة الخاصة بك غير صحيحة.", "your_old_password_incorrect": "كلمة المرور القديمة الخاصة بك غير صحيحة.",
"failed_to_update_password": "", "failed_to_update_password": "",
@ -82,5 +82,13 @@
"password_between_8_to_40": "يجب أن تتراوح كلمة المرور بين 8 و64 حرفًا", "password_between_8_to_40": "يجب أن تتراوح كلمة المرور بين 8 و64 حرفًا",
"new_password_required": "كلمة المرور الجديدة مطلوبة", "new_password_required": "كلمة المرور الجديدة مطلوبة",
"old_password_is_required": "كلمة المرور القديمة مطلوبة", "old_password_is_required": "كلمة المرور القديمة مطلوبة",
"confirm_password_is_required": "تأكيد كلمة المرور مطلوب" "confirm_password_is_required": "تأكيد كلمة المرور مطلوب",
"kpiCards": "يمكنك عرض بطاقات مؤشرات الأداء الرئيسية التفاعلية والرسوم البيانية لاستكشاف اتجاهات البيانات ومقارنة المقاييس الرئيسية بصريًا",
"home_topic": "اختر أي فئة (على سيبل المثال : الاقتصاد، الاجتماعية، البيئة) لاستكشاف مؤشرات الأداء الرئيسية التفصيلية ، التصورات، ورؤى البيانات .",
"economy": "يمكنك الوصول إلى الإحصائيات التفصيلية وصفحات التحليل لمؤشرات الأداء الرئيسية في فئة الاقتصاد لاكتشاف الاتجاهات والمقاييس الرئيسية .",
"bookMark": "يمكنك وضع إشارة مرجعية على مؤشرات الأداء الرئيسية أو مجموعات البيانات المهمة للوصول السريع والسهل في أي وقت من خلال قسم الإشارات المرجعية. ",
"toggle": "يمكنك التبديل بين اللغتين الإنجليزية والعربية باستخدام مفتاح تبديل اللغة في الزاوية اليمنى العليا للحصول على تجربة ثنائية اللغة.",
"tour_navBar": "يمكنك استخدام شريط التنقل للوصول السريع إلى الملف الشخصي ,التعليقات , دليل المستخدم , الإشعارات والميزات الأساسية الأخرى للتطبيق.",
"mainMenu": "يمكنك الوصول إلى الصفحات المخصصة والميزات الإضافية عن طريق تحديد الخيارات من القائمة الرئيسية."
} }

View File

@ -8,17 +8,19 @@
"register_details": "Please enter your details", "register_details": "Please enter your details",
"register_name": "Username", "register_name": "Username",
"email_id": "Email ID", "email_id": "Email ID",
"enter_name": "Enter your User Name", "enter_name": "Enter your User Name",
"register_email": "Enter your email", "enter_your_email": "Enter your email",
"register_password": "Enter your password", "enter_your_password": "Enter your password",
"register_Confirm_password": "Confirm password", "register_Confirm_password": "Confirm password",
"agree": "I agree to ", "agree": "I agree to the ",
"t_and": "and", "t_and": " and ",
"conditions": " of Fcsc", "conditions": " of FCSC",
"terms_conditions": "terms & conditions", "terms_conditions": "Terms & Conditions",
"privacy_policy":"privacy policy", "privacy_policy":"Privacy Policy",
"account_confirmation": "Already have an account? Login", "account_confirmation": "Already have an account?",
"login_title": "Login", "login_title": "Login",
"profile_title": "Edit Profile", "profile_title": "Edit Profile",
"my_profile": "My Profile", "my_profile": "My Profile",
@ -83,5 +85,13 @@
"password_between_8_to_40": "Password must be between 8 and 64 characters", "password_between_8_to_40": "Password must be between 8 and 64 characters",
"new_password_required": "New password is required", "new_password_required": "New password is required",
"old_password_is_required": "Old password is required", "old_password_is_required": "Old password is required",
"confirm_password_is_required": "Confirm password is required" "confirm_password_is_required": "Confirm password is required",
"kpiCards": "View interactive KPI cards and graphs to explore data trends and compare key metrics visually.",
"home_topic": "Tap on any category (e.g., Economy, Social, Environment) to explore detailed KPIs, visualizations, and data insights.",
"economy": "Access detailed statistics and drill-down pages for KPIs in the Economy category to uncover key trends and metrics.",
"bookMark": "Bookmark important KPIs or datasets for quick and easy access anytime through the Bookmarks section.",
"toggle": "Switch between English and Arabic using the language toggle at the top-right corner for a bilingual experience.",
"tour_navBar": "Access dedicated pages and additional features by selecting options from the Main Menu.",
"mainMenu": "Use the navigation bar to quickly access Profile, Feedback, User Guide, Notifications, and other key app features."
} }

View File

@ -106,13 +106,13 @@ class MainApp extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
final locale = ref.watch(localeProvider); final locale = ref.watch(localeProvider);
ref.listen<bool>(connectivityProvider, (previous, hasInternet) { // ref.listen<bool>(connectivityProvider, (previous, hasInternet) {
print('Previous Internet Status: $previous'); // print('Previous Internet Status: $previous');
print('Current Internet Status: $hasInternet'); // print('Current Internet Status: $hasInternet');
if (previous != null && hasInternet != previous) { // if (previous != null && hasInternet != previous) {
handleConnectivityChange(context, hasInternet); // handleConnectivityChange(context, hasInternet);
} // }
}); // });
return MaterialApp.router( return MaterialApp.router(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,

View File

@ -1,12 +1,626 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
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/presentation/components/constant/constant.dart'; import 'package:uae_stat/presentation/components/constant/constant.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart';
class Marriages extends ConsumerStatefulWidget {
class Marriages extends StatelessWidget {
const Marriages({super.key}); const Marriages({super.key});
@override
ConsumerState<Marriages> createState() => _MarriagesState();
}
class _MarriagesState extends ConsumerState<Marriages> {
late List<TargetFocus> marriageTargets;
late List<TargetFocus> previousMarriageTargets;
late TutorialCoachMark tutorialCoachMark;
final GlobalKey chartKey = GlobalKey();
final GlobalKey bookMarkKey = GlobalKey();
late double cardHeight= 0;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_calculateMarkPosition();
_startTutorialAfterRender();
});
}
void handleSkip() {
tutorialCoachMark.skip();
debugPrint('Skip clicked');
ref.read(chartsTourProvider.notifier).state = true;
ref.read(previousChartsTourProvider.notifier).state = true;
ref.read(homeTourProvider.notifier).state = true;
ref.read( previousHomeTourProvider.notifier).state = true;
ref.read(scaffoldTourProvider.notifier).state = true;
ref.read(previousScaffoldTourProvider.notifier).state = true;
context.go('/myhomepage');
}
void _kpiTutorialCoachMark() {
_initmarriageTargets();
tutorialCoachMark = TutorialCoachMark(
paddingFocus: 0,
useSafeArea: true,
pulseEnable: false,
hideSkip: true,
targets: marriageTargets,
onClickTarget: (target) {
if (target.identify == 'chartKey') {
// Scroll to the long widget
Scrollable.ensureVisible(
chartKey.currentContext!,
duration: Duration(milliseconds: 500),
);
}
},
onFinish: () {
context.push('/myhomepage');
debugPrint('Marriage Tutorial Finished');
},
)..show(context: context);
}
void _previousKpiTutorial() {
_initpreviousTargets();
tutorialCoachMark = TutorialCoachMark(
paddingFocus: 0,
useSafeArea: true,
pulseEnable: false,
hideSkip: true,
targets: previousMarriageTargets,
onFinish: () {
context.push('/myhomepage');
debugPrint('Previous Marriage Tutorial Finished');
},
)..show(context: context);
}
void _startTutorialAfterRender() {
final chartTour = ref.watch(chartsTourProvider);
final previousChartTour = ref.watch(previousChartsTourProvider);
if (chartKey.currentContext != null && !chartTour) {
_kpiTutorialCoachMark();
} else if(!previousChartTour) {
_previousKpiTutorial();
}
else if (!chartTour || !previousChartTour){
Future.delayed(Duration(milliseconds: 100), _startTutorialAfterRender);
}else{
return;
}
}
void _calculateMarkPosition() {
final RenderBox cardRenderBox = bookMarkKey.currentContext!.findRenderObject() as RenderBox;
final Size cardSize = cardRenderBox.size;
cardHeight = cardSize.height;
debugPrint('height : $cardHeight');
}
void _initmarriageTargets(){
final double screenWidth= MediaQuery.of(context).size.width;
final double screenHeight= MediaQuery.of(context).size.height;
marriageTargets=[
TargetFocus(
identify: 'chartsKey',
keyTarget: chartKey,
shape: ShapeLightFocus.RRect,
contents: [
createTargetContent(
text: AppLocalizations.of(context)!.kpiCards,
alignment: ContentAlign.top,
gap:50,
space: 0,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height*0.3,
child: Stack(
children: [
Positioned(
bottom: 25,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () => handleSkip(),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
child: const Text(
'Skip',
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
Column(
children: [
Text(
'3/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
const SizedBox(height: 3),
Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
ref.read(chartsTourProvider.notifier).state=true;
ref.read(previousHomeTourProvider.notifier).state=false;
tutorialCoachMark.finish();
},
),
),
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(color: Color(0xFF7DAFBC), width: 1.5),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward, color: Colors.white),
onPressed: () {
tutorialCoachMark.next();
},
),
),
],
),
],
),
],
),
),
],
),
),
),
TargetContent(
padding: EdgeInsets.only(bottom: 0,top : 0,),
align: ContentAlign.top,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height:MediaQuery.of(context).size.height*0.69 ,
child: Stack(
children: [
Positioned(
left: MediaQuery.of(context).size.width*0.40,
bottom: 0,
child: Image.asset(
'assets/app_tour/leftUp.png',
width: 100,
height: 90,
),
),
]
),
),
),
],
),
TargetFocus(
identify: 'BookMarkKey',
keyTarget: bookMarkKey,
shape: ShapeLightFocus.RRect,
contents: [
createTargetContent(
text: AppLocalizations.of(context)!.bookMark,
alignment: ContentAlign.bottom,
gap: 23,
space: 50,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height*0.60,
child: Stack(
children: [
Positioned(
bottom: 35,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () => handleSkip(),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
child: const Text(
'Skip',
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
Column(
children: [
Text(
'4/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
const SizedBox(height: 3),
Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
tutorialCoachMark.previous();
},
),
),
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(color: Color(0xFF7DAFBC), width: 1),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward, color: Colors.white),
onPressed: () {
ref.read(chartsTourProvider.notifier).state=true;
ref.read(scaffoldTourProvider.notifier).state=false;
tutorialCoachMark.finish();
},
),
),
],
),
],
),
],
),
),
],
),
),
),
TargetContent(
padding: EdgeInsets.only(left:0, top: cardHeight+20),
align: ContentAlign.right,
child: Container(
width: screenWidth/4,
height: screenHeight,
child: Stack(
children: [
Image.asset(
'assets/app_tour/leftDown.png',
fit: BoxFit.contain,
),
// Positioned(
// top: 0,
// left: MediaQuery.of(context).size.width* 0.5,
// child: SizedBox(
// width: 50,
// height: 100,
// child: Image.asset(
// 'assets/app_tour/bookmark2.png',
// fit: BoxFit.contain,
// ),
// ),
// ),
],
),
),
),
],
),
];
}
void _initpreviousTargets(){
final double screenWidth= MediaQuery.of(context).size.width;
final double screenHeight= MediaQuery.of(context).size.height;
previousMarriageTargets=[
TargetFocus(
identify: 'BookMarkKey',
keyTarget: bookMarkKey,
shape: ShapeLightFocus.RRect,
contents: [
createTargetContent(
text: AppLocalizations.of(context)!.bookMark,
space: 50,
alignment: ContentAlign.bottom,
gap: 23,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height*0.60,
child: Stack(
children: [
Positioned(
bottom: 35,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () => handleSkip(),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
child: const Text(
'Skip',
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
Column(
children: [
Text(
'4/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
const SizedBox(height: 3),
Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
tutorialCoachMark.next();
},
),
),
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(color:Color(0xFF7DAFBC), width: 1.5),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward, color: Colors.white),
onPressed: () {
ref.read(chartsTourProvider.notifier).state=true;
ref.read(scaffoldTourProvider.notifier).state=false;
tutorialCoachMark.finish();
},
),
),
],
),
],
),
],
),
),
],
),
),
),
TargetContent(
padding: EdgeInsets.only(left:0, top: cardHeight+20),
align: ContentAlign.right,
child: Container(
width: screenWidth/4,
height: screenHeight,
child: Stack(
children: [
Image.asset(
'assets/app_tour/leftDown.png',
fit: BoxFit.contain,
),
// Positioned(
// top: 0,
// left: MediaQuery.of(context).size.width* 0.5,
// child: SizedBox(
// width: 50,
// height: 100,
// child: Image.asset(
// 'assets/app_tour/bookmark2.png',
// fit: BoxFit.contain,
// ),
// ),
// ),
],
),
),
),
],
),
TargetFocus(
identify: 'chartsKey',
keyTarget: chartKey,
shape: ShapeLightFocus.RRect,
contents: [
createTargetContent(
text: AppLocalizations.of(context)!.kpiCards,
space: 0,
alignment: ContentAlign.top,
gap:50,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height*0.3,
child: Stack(
children: [
Positioned(
bottom: 30,
left: 16,
right: 16,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () => handleSkip(),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
child: const Text(
'Skip',
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
Column(
children: [
Text(
'3/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
const SizedBox(height: 3),
Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () {
ref.read(previousChartsTourProvider.notifier).state=true;
ref.read(previousHomeTourProvider.notifier).state=false;
tutorialCoachMark.finish();
},
),
),
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(color:Color(0xFF7DAFBC), width: 1.5),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward, color: Colors.white),
onPressed: () {
tutorialCoachMark.previous();
},
),
),
],
),
],
),
],
),
),
],
),
),
),
TargetContent(
padding: EdgeInsets.only(bottom: 0,top : 0,),
align: ContentAlign.top,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height:MediaQuery.of(context).size.height*0.69 ,
child: Stack(
children: [
Positioned(
left: MediaQuery.of(context).size.width*0.40,
bottom: 0,
child: Image.asset(
'assets/app_tour/leftUp.png',
width: 100,
height: 90,
),
),
]
),
),
),
],
),
];
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double myheight = MediaQuery.of(context).size.height; double myheight = MediaQuery.of(context).size.height;
@ -33,7 +647,7 @@ class Marriages extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
IconButton(onPressed: (){}, icon: Icon(Icons.bookmark_add_outlined,color: Colors.white,size: 40,)), IconButton(key :bookMarkKey,onPressed: (){}, icon: Icon(Icons.bookmark_add_outlined,color: Colors.white,size: 40,)),
IconButton(onPressed: (){}, icon: Icon(Icons.share,color: Colors.white,size: 30,)), IconButton(onPressed: (){}, icon: Icon(Icons.share,color: Colors.white,size: 30,)),
],), ],),
], ],
@ -47,6 +661,7 @@ class Marriages extends StatelessWidget {
child: Column( child: Column(
children: [ children: [
Row( Row(
key:chartKey,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [ children: [
Container(height: myheight/3.5,width: mywidth/2.5, Container(height: myheight/3.5,width: mywidth/2.5,

View File

@ -1,7 +1,9 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/presentation/components/constant/constant.dart'; import 'package:uae_stat/presentation/components/constant/constant.dart';
@ -23,28 +25,31 @@ class UaeNumbers extends StatelessWidget {
} }
} }
class uaenumberWidget extends StatefulWidget { class uaenumberWidget extends ConsumerStatefulWidget {
const uaenumberWidget({super.key}); const uaenumberWidget({super.key});
@override @override
_UaenumberWidgetState createState() => _UaenumberWidgetState(); ConsumerState<uaenumberWidget> createState() => _UaenumberWidgetState();
} }
class _UaenumberWidgetState extends State<uaenumberWidget> { class _UaenumberWidgetState extends ConsumerState<uaenumberWidget> {
@override @override
List<dynamic> homePageData = []; List<dynamic> homePageData = [];
bool isLoading = true; bool isLoading = true;
int? expandedIndex = 0;
@override @override
void initState() { void initState() {
fetchData(); super.initState();
final locale = ref.read(localeProvider);
fetchData(locale?.languageCode ?? 'en');
} }
Future<void> fetchData() async { Future<void> fetchData(locale) async {
// const baseUrl = 'https://pb.venbait.in/api/getHomePageData'; // const baseUrl = 'https://pb.venbait.in/api/getHomePageData';
const baseUrl = 'https://pb.venbait.in/api/getUAENumbersData'; const baseUrl = 'https://pb.venbait.in/api/getUAENumbersData';
try { try {
final response = await http.get(Uri.parse(baseUrl)); final response = await http.get(Uri.parse(baseUrl + '?language=$locale'));
if (response.statusCode == 200) { if (response.statusCode == 200) {
setState(() { setState(() {
homePageData = json.decode(response.body); homePageData = json.decode(response.body);
@ -63,6 +68,10 @@ class _UaenumberWidgetState extends State<uaenumberWidget> {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
fetchData(localeCode);
});
double myheight = MediaQuery.of(context).size.height; double myheight = MediaQuery.of(context).size.height;
double mywidth = MediaQuery.of(context).size.width; double mywidth = MediaQuery.of(context).size.width;
@ -74,12 +83,13 @@ class _UaenumberWidgetState extends State<uaenumberWidget> {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.white,
borderRadius: BorderRadius.circular(30.0), borderRadius: BorderRadius.circular(30.0),
border: Border.all(width: 1)), border: Border.all(width: 1,color: Color(0xFFAA8E83)),),
child: TextField( child: TextField(
decoration: InputDecoration( decoration: InputDecoration(
hintText: "Search", hintText: "Search",
prefixIcon: Icon(Icons.search), prefixIcon: Icon(Icons.search,
border: InputBorder.none, color: Color(0xFFAA8E83),),
border: InputBorder.none,
contentPadding: contentPadding:
EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0), EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0),
), ),
@ -99,7 +109,12 @@ class _UaenumberWidgetState extends State<uaenumberWidget> {
return CustomExpandableTile( return CustomExpandableTile(
index: index, index: index,
isExpanded: isFirstTile, isExpanded: expandedIndex == index, // Compare with expandedIndex
onTap: (index) {
setState(() {
expandedIndex = (expandedIndex == index) ? null : index;
});
},
title: mainTopic['main_topic'], title: mainTopic['main_topic'],
titleBackgroundColor: backgroundColor, titleBackgroundColor: backgroundColor,
children: _buildSubTopics(mainTopic['sub_topics'] ?? [], mywidth, children: _buildSubTopics(mainTopic['sub_topics'] ?? [], mywidth,
@ -280,6 +295,8 @@ class CustomExpandableTile extends StatefulWidget {
final List<Widget> children; final List<Widget> children;
final int index; final int index;
final bool isExpanded; final bool isExpanded;
final ValueChanged<int> onTap;
const CustomExpandableTile({ const CustomExpandableTile({
required this.title, required this.title,
@ -287,6 +304,8 @@ class CustomExpandableTile extends StatefulWidget {
required this.children, required this.children,
required this.index, required this.index,
required this.isExpanded, required this.isExpanded,
required this.onTap,
}); });
@override @override
@ -294,14 +313,12 @@ class CustomExpandableTile extends StatefulWidget {
} }
class _CustomExpandableTileState extends State<CustomExpandableTile> { class _CustomExpandableTileState extends State<CustomExpandableTile> {
// bool isExpanded = false; bool isExpanded = false;
late bool isExpanded; // late bool isExpanded;
@override @override
void initState() { void initState() {
super.initState(); super.initState(); // Initialize isExpanded based on widget's property
isExpanded =
widget.isExpanded; // Initialize isExpanded based on widget's property
} }
@override @override
@ -312,11 +329,8 @@ class _CustomExpandableTileState extends State<CustomExpandableTile> {
child: Column( child: Column(
children: [ children: [
GestureDetector( GestureDetector(
onTap: () { onTap: () => widget.onTap(widget.index),
setState(() {
isExpanded = !isExpanded;
});
},
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: widget.titleBackgroundColor, color: widget.titleBackgroundColor,
@ -335,7 +349,7 @@ class _CustomExpandableTileState extends State<CustomExpandableTile> {
), ),
), ),
Icon( Icon(
isExpanded widget.isExpanded
? Icons.keyboard_arrow_up ? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down, : Icons.keyboard_arrow_down,
color: Colors.white, color: Colors.white,
@ -350,8 +364,8 @@ class _CustomExpandableTileState extends State<CustomExpandableTile> {
duration: Duration(milliseconds: 300), duration: Duration(milliseconds: 300),
curve: Curves.easeInOut, curve: Curves.easeInOut,
width: double.infinity, width: double.infinity,
height: isExpanded ? myheight * 0.52 : 0, height: widget.isExpanded ? myheight * 0.52 : 0,
child: isExpanded child: widget.isExpanded
? SingleChildScrollView( ? SingleChildScrollView(
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(

View File

@ -0,0 +1,50 @@
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
import 'package:flutter/material.dart';
final allTargetsCompletedProvider = StateProvider<bool>((ref) => true);
final chartsTourProvider = StateProvider<bool>((ref) => true);
final previousChartsTourProvider = StateProvider<bool>((ref) => true);
final homeTourProvider = StateProvider<bool>((ref) => false);
final previousHomeTourProvider = StateProvider<bool>((ref) => true);
final scaffoldTourProvider = StateProvider<bool>((ref) => true);
final previousScaffoldTourProvider = StateProvider<bool>((ref) => true);
TargetContent createTargetContent({
required String text,
required ContentAlign alignment,
required double gap,
required double space ,
}) {
return TargetContent(
align: alignment,
builder: (context, controller) {
return Stack(
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.only(left: 10, right: 10, bottom: space , top: space ),
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(12),
),
child: Text(
text,
style: const TextStyle(
color: Colors.white,
fontSize: 18,
),
textAlign: TextAlign.center,
),
),
SizedBox(height: gap,),
],
),
],
);
},
);
}

View File

@ -13,7 +13,9 @@ import '../../../config/my_theme.dart';
class CreateNewPw extends StatefulWidget { class CreateNewPw extends StatefulWidget {
final String userId; final String userId;
final String email; 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 @override
State<CreateNewPw> createState() => _CreateNewPwState(); State<CreateNewPw> createState() => _CreateNewPwState();
@ -205,7 +207,38 @@ class _CreateNewPwState extends State<CreateNewPw> {
padding: const EdgeInsets.all(24.0), padding: const EdgeInsets.all(24.0),
child: Column( child: Column(
children: [ children: [
// SizedBox(height: screenHeight / 7),
Align(
alignment: Alignment.topRight,
child: GestureDetector(
onTap: () {
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
width: 30, // Circle diameter
height: 30,
decoration: BoxDecoration(
color: Colors.grey[300], // Circle color
shape: BoxShape.circle,
),
child: Icon(
Icons.close,
size: 20, // Icon size
color: Colors.white, // Icon color
),
),
),
),
SizedBox(height: screenHeight / 6), SizedBox(height: screenHeight / 6),
Text( Text(
// "Create New Password", // "Create New Password",
AppLocalizations.of(context)!.create_new_password, AppLocalizations.of(context)!.create_new_password,
@ -327,8 +360,8 @@ class _CreateNewPwState extends State<CreateNewPw> {
SizedBox(height: screenHeight / 5), SizedBox(height: screenHeight / 5),
Center( Center(
child: Container( child: Container(
height: screenHeight / 8, height: screenHeight / 12,
width: screenWidth / 2, width: screenWidth / 2.5,
decoration: BoxDecoration( decoration: BoxDecoration(
image: DecorationImage( image: DecorationImage(
image: AssetImage("assets/splash_screen/logo.png"), image: AssetImage("assets/splash_screen/logo.png"),

File diff suppressed because it is too large Load Diff

View File

@ -1035,49 +1035,47 @@
// //
// [ // [
// { // {
// chart_heading :"Guest Nights by Region" // chart_heading :"Population Growth Over Time"
// chart_type :"fl_stacked_bar" // chart_type :"line_trend"
// dataset :"hotels" // dataset :"population"
// group_by :"GUEST_REGION" // group_by :"GENDER"
// is_chart :"true" // is_chart :"true"
// kpi :"hotel_occupancy_rate_chart_1" // kpi :"population"
// main_id :"economy" // main_id :"social"
// response:[ // response:[
// { // {
// "ObsKey": { // "ObsKey": {
// "FREQ": "A", // "FREQ": "A",
// "GUEST_REGION": "AF", // "GENDER": "M",
// "H_INDICATOR": "GUN", // "MEASURE": "POP",
// "H_TYPE": "_Z", // "POP_IND": "_Z",
// "MEASURE": "H",
// "REF_AREA": "AE", // "REF_AREA": "AE",
// "SOURCE_DETAIL": "FCSC", // "SOURCE_DETAIL": "FCSC",
// "TIME_PERIOD": "2016", // "TIME_PERIOD": "1970",
// "UNIT_MEASURE": "NUMBER" // "UNIT_MEASURE": "PS"
// }, // },
// "ObsValue": { // "ObsValue": {
// "Value": "2659446" // "Value": "149195"
// }
// } // }
// },
// { // {
// "ObsKey": { // "ObsKey": {
// "FREQ": "A", // "FREQ": "A",
// "GUEST_REGION": "OC", // "GENDER": "F",
// "H_INDICATOR": "GUN", // "MEASURE": "POP",
// "H_TYPE": "_Z", // "POP_IND": "_Z",
// "MEASURE": "H",
// "REF_AREA": "AE", // "REF_AREA": "AE",
// "SOURCE_DETAIL": "FCSC", // "SOURCE_DETAIL": "FCSC",
// "TIME_PERIOD": "2018", // "TIME_PERIOD": "1995",
// "UNIT_MEASURE": "NUMBER" // "UNIT_MEASURE": "PS"
// }, // },
// "ObsValue": { // "ObsValue": {
// "Value": "1127282" // "Value": "850654"
// } // }
// } // }
// //
// ] // ]
// sub_id :"tourism" // sub_id :"population"
// 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" // url :"https://releaseeuaestat.fcsc.gov.ae/rest/data/FCSA,DF_POP,2.7.0/....A..?startPeriod=1970&dimensionAtObservation=AllDimensions"
// } // }
// ] // ]

File diff suppressed because it is too large Load Diff

View File

@ -13,13 +13,13 @@ class ChartData {
class ApiService { class ApiService {
static const String baseUrl = 'https://pb.venbait.in/api/getDataSet'; static const String baseUrl = 'https://pb.venbait.in/api/getDataSet';
Future<Map<String, dynamic>> fetchChartData(String dataSets) async { Future<Map<String, dynamic>> fetchChartData(String dataSets, locale) async {
List<dynamic> isChartData = []; List<dynamic> isChartData = [];
List<dynamic> nonChartData = []; List<dynamic> nonChartData = [];
List<dynamic> originalChartsData = []; List<dynamic> originalChartsData = [];
List<dynamic> originalCardData = []; List<dynamic> originalCardData = [];
final url = Uri.parse('$baseUrl?dataset=$dataSets'); final url = Uri.parse('$baseUrl?dataset=$dataSets&language=$locale');
try { try {
final response = await http.get(url); final response = await http.get(url);
@ -43,7 +43,8 @@ class ApiService {
return { return {
'isChartData': isChartData, 'isChartData': isChartData,
'nonChartData': nonChartData, 'nonChartData': nonChartData,
'filterData': jsonData['filter_data'] 'filterData': jsonData['new_filter_data']
// 'filterData': jsonData['filter_data']
// 'originalChartsData': originalChartsData, // 'originalChartsData': originalChartsData,
// 'originalCardData': originalCardData, // 'originalCardData': originalCardData,
}; };

File diff suppressed because it is too large Load Diff

View File

@ -10,6 +10,7 @@ import 'package:image_picker/image_picker.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:pocketbase/pocketbase.dart'; import 'package:pocketbase/pocketbase.dart';
import 'package:uae_stat/domain/use_cases/language.dart'; import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/icons/misc_icon_asset_path.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import '../components/my_bottom_nav_bar.dart'; import '../components/my_bottom_nav_bar.dart';
@ -74,6 +75,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
// Regular expression to validate Full Name (no special characters) // 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; late Future<RecordModel> userDetails;
bool _isHoveringDate = false;
bool _isHoveringDropdown = false;
@override @override
void initState() { void initState() {
@ -334,418 +337,450 @@ class _ProfileScreenState extends State<ProfileScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return PopScope(
appBar: AppBar( onPopInvokedWithResult: (didPop, result) {
backgroundColor: Colors.white, if (didPop) return;
elevation: 0, context.go('/');
leading: IconButton( },
icon: Icon(Icons.arrow_back_ios_new, color: Colors.black), child: Scaffold(
onPressed: () { appBar: AppBar(
context.go('/'); backgroundColor: Colors.white,
}, elevation: 0,
), leading: IconButton(
title: Text( icon: Icon(Icons.arrow_back_ios_new, color: Colors.black),
'My Profile',
style: TextStyle(color: Colors.orange),
),
actions: [
TextButton(
onPressed: () { onPressed: () {
context.go('/'); context.go('/');
}, },
child: Text( ),
'Logout', title: Text(
style: TextStyle(color: Colors.orange), 'My Profile',
style: TextStyle(color: Color(0xFF985400)),
),
actions: [
TextButton(
onPressed: () {
context.go('/');
},
child: Text(
'Logout',
style: TextStyle(color: Colors.orange),
),
), ),
), IconButton(
IconButton( icon: Icon(Icons.logout, color: Colors.black),
icon: Icon(Icons.logout, color: Colors.black), onPressed: () {},
onPressed: () {}, ),
), ],
], ),
), body: SingleChildScrollView(
body: SingleChildScrollView( child: Form(
child: Form( key: _formKey,
key: _formKey, child: Padding(
child: Padding( padding: const EdgeInsets.only(
padding: const EdgeInsets.all(20.0), top: 20.0, bottom: 20.0, left: 25, right: 25),
child: Column( child: Column(
children: [ children: [
// CircleAvatar( // CircleAvatar(
// radius: 50, // radius: 50,
// backgroundImage: _profileImage != null // backgroundImage: _profileImage != null
// ? FileImage(_profileImage!) // ? FileImage(_profileImage!)
// : AssetImage("assets/edit_profile/profile.png") // : AssetImage("assets/edit_profile/profile.png")
// as ImageProvider, // as ImageProvider,
// child: Align( // child: Align(
// alignment: Alignment.bottomRight, // alignment: Alignment.bottomRight,
// child: GestureDetector( // child: GestureDetector(
// onTap: _pickImage, // Call `_pickImage` on tap // onTap: _pickImage, // Call `_pickImage` on tap
// child: CircleAvatar( // child: CircleAvatar(
// radius: 15, // radius: 15,
// backgroundColor: Colors.white, // backgroundColor: Colors.white,
// child: Icon( // child: Icon(
// Icons.camera_alt, // Icons.camera_alt,
// size: 15, // size: 15,
// color: Colors.grey, // color: Colors.grey,
// ), // ),
// ), // ),
// ), // ),
// ), // ),
// ), // ),
Stack( Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
// CircleAvatar with the profile image // CircleAvatar with the profile image
CircleAvatar( CircleAvatar(
radius: 50, radius: 50,
backgroundImage: _profileImage != null backgroundImage: _profileImage != null
? FileImage(_profileImage!) ? FileImage(_profileImage!)
: AssetImage("assets/edit_profile/profile.png") : AssetImage("assets/edit_profile/profile.png")
as ImageProvider, as ImageProvider,
child: Align( child: Align(
alignment: Alignment.bottomRight, alignment: Alignment.bottomRight,
child: GestureDetector( child: GestureDetector(
onTap: _pickImage, // Call `_pickImage` on tap onTap: _pickImage, // Call `_pickImage` on tap
child: CircleAvatar( child: CircleAvatar(
radius: 15, radius: 15,
backgroundColor: Colors.white, backgroundColor: Colors.white,
child: Icon( child: Icon(
Icons.camera_alt, Icons.camera_alt,
size: 15, size: 15,
color: Colors.grey, color: Colors.grey,
),
), ),
), ),
), ),
), ),
),
// Conditional loader that shows when _isLoading is true // Conditional loader that shows when _isLoading is true
if (_isLoading) if (_isLoading)
Positioned( Positioned(
child: CircularProgressIndicator( child: CircularProgressIndicator(
strokeWidth: 2, strokeWidth: 2,
valueColor: valueColor:
AlwaysStoppedAnimation<Color>(Colors.grey), AlwaysStoppedAnimation<Color>(Colors.grey),
),
), ),
), ],
], ),
),
SizedBox(height: 20), SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"User Name",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.grey),
),
],
),
SizedBox(height: 10),
TextFormField(
controller: _usernameController,
focusNode: _focusNodes[0],
decoration: InputDecoration(
hintText: _showHints[0] ? 'Enter the User Name' : null,
hintStyle: TextStyle(color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
enabled: false,
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Email ID",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.grey),
),
],
),
SizedBox(height: 10),
TextFormField(
controller: _emailController,
focusNode: _focusNodes[1],
decoration: InputDecoration(
hintText:
_showHints[1] ? 'mohammad.hassan@fcsc.gov.ae' : null,
hintStyle: TextStyle(color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
enabled: false,
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Full Name*",
style:
TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
],
),
SizedBox(height: 10),
TextFormField(
enabled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Required';
}
//RegExp(r"^[a-zA-Z\s]+$");
final nameRegex = RegExp(
r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$");
if (!nameRegex.hasMatch(value)) {
return 'Invalid Characters';
}
return null;
},
controller: _fullNameController,
focusNode: _focusNodes[2],
decoration: InputDecoration(
hintText: _showHints[2] ? 'Enter the name' : null,
hintStyle: TextStyle(color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
counterText: '',
),
maxLength: 40, // Set the maximum length to 20 characters
maxLengthEnforcement: MaxLengthEnforcement.enforced,
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Date of Birth*",
style:
TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
],
),
SizedBox(height: 10),
TextFormField(
controller: _dateController,
focusNode: _focusNodes[3],
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
hintText: _showHints[3] ? 'DD/MM/YYYY' : null,
//_showHints[3] ? 'Select your Date of Birth' : null,
hintStyle: TextStyle(color: Colors.grey),
suffixIcon: const Icon(Icons.arrow_drop_down_sharp),
),
readOnly: true,
onTap: _pickDate,
validator: _validateDob,
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Country/Region*",
style:
TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
),
],
),
SizedBox(height: 10),
DropdownButtonFormField<String>(
value: _selectedCountry,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
labelText: 'Select',
),
items: _countries
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(item),
))
.toList(),
onChanged: (String? newValue) {
setState(() {
_selectedCountry = newValue;
});
},
validator: _validateDropdown,
),
SizedBox(height: 20),
Row(
children: [
Checkbox(
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = value ?? false;
showError = false;
});
},
side: BorderSide(
color: showError ? Colors.red : Colors.grey,
width: 1.5,
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 10,
),
Text.rich(
TextSpan(
text: 'I agree to the ',
style: TextStyle(color: Colors.black),
children: [
TextSpan(
text: 'Terms & Conditions',
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// Add action for Terms & Conditions tap
},
),
TextSpan(
text: ' and ',
style: TextStyle(color: Colors.black),
),
TextSpan(
text: 'Privacy Policy',
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// Add action for Privacy Policy tap
},
),
TextSpan(
text: ' of FCSC.',
style: TextStyle(color: Colors.black),
),
],
),
textAlign: TextAlign.start,
maxLines: 2,
overflow: TextOverflow.visible,
softWrap: true,
),
],
),
),
],
),
if (showError)
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
Padding( Text(
padding: const EdgeInsets.only(left: 10.0), "User Name",
child: Text( style: TextStyle(
'Please agree to terms and conditions', fontSize: 16,
style: TextStyle( fontWeight: FontWeight.w500,
color: Colors.red[700], color: Colors.grey),
fontSize: 12, ),
],
),
SizedBox(height: 10),
TextFormField(
controller: _usernameController,
focusNode: _focusNodes[0],
decoration: InputDecoration(
// hintText: _showHints[0] ? 'Enter the User Name' : null,
hintStyle: TextStyle(color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
enabled: false,
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Email ID",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w500,
color: Colors.grey),
),
],
),
SizedBox(height: 10),
TextFormField(
controller: _emailController,
focusNode: _focusNodes[1],
decoration: InputDecoration(
// hintText:
// _showHints[1] ? 'mohammad.hassan@fcsc.gov.ae' : null,
hintStyle: TextStyle(color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
enabled: false,
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Full Name*",
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.w500),
),
],
),
SizedBox(height: 10),
TextFormField(
enabled: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Required';
}
//RegExp(r"^[a-zA-Z\s]+$");
final nameRegex = RegExp(
r"^[a-zA-Z0-9'\-\u0627\u0639~\u00C0-\u00FF\s]+$");
if (!nameRegex.hasMatch(value)) {
return 'Invalid Characters';
}
return null;
},
controller: _fullNameController,
focusNode: _focusNodes[2],
decoration: InputDecoration(
hintText: _showHints[2] ? 'Enter the name' : null,
hintStyle: TextStyle(color: Colors.grey),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
counterText: '',
),
maxLength: 40, // Set the maximum length to 20 characters
maxLengthEnforcement: MaxLengthEnforcement.enforced,
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Date of Birth*",
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.w500),
),
],
),
SizedBox(height: 10),
MouseRegion(
onEnter: (_) => setState(() => _isHoveringDate = true),
onExit: (_) => setState(() => _isHoveringDate = false),
child: TextFormField(
controller: _dateController,
focusNode: _focusNodes[3],
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
hintText: _showHints[3] ? 'DD/MM/YYYY' : null,
//_showHints[3] ? 'Select your Date of Birth' : null,
hintStyle: TextStyle(color: Colors.grey),
suffixIcon: Container(
width: 45,
padding: EdgeInsets.only(right: 1),
alignment:
Alignment.center, // Center the icon vertically
child: Icon(
Icons.keyboard_arrow_down_sharp,
color: _isHoveringDate ? Colors.black : Colors.grey,
), ),
), ),
), ),
readOnly: true,
onTap: _pickDate,
validator: _validateDob,
),
),
SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
"Country/Region*",
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.w500),
),
],
),
SizedBox(height: 10),
MouseRegion(
onEnter: (_) => setState(() => _isHoveringDropdown = true),
onExit: (_) => setState(() => _isHoveringDropdown = false),
child: DropdownButtonFormField<String>(
value: _selectedCountry,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
),
labelText: 'Select',
),
icon: Icon(Icons.keyboard_arrow_down_sharp,
color:
_isHoveringDropdown ? Colors.black : Colors.grey),
items: _countries
.map((item) => DropdownMenuItem<String>(
value: item,
child: Text(item),
))
.toList(),
onChanged: (String? newValue) {
setState(() {
_selectedCountry = newValue;
});
},
validator: _validateDropdown,
),
),
SizedBox(height: 20),
Row(
children: [
Checkbox(
value: isChecked,
onChanged: (value) {
setState(() {
isChecked = value ?? false;
showError = false;
});
},
side: BorderSide(
color:
showError ? Color(0xFFb22222) : Color(0xFF92722A),
width: 1,
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 10,
),
Text.rich(
TextSpan(
text: 'I agree to the ',
style: TextStyle(color: Colors.black),
children: [
TextSpan(
text: 'Terms & Conditions',
style: TextStyle(
color: Color(0xFF648CBA),
decoration: TextDecoration.underline,
fontWeight: FontWeight
.bold, // Makes the text bold
decorationColor: Color(0xFF648CBA),
decorationThickness: 1,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// Add action for Terms & Conditions tap
},
),
TextSpan(
text: ' and ',
style: TextStyle(color: Colors.black),
),
TextSpan(
text: 'Privacy Policy',
style: TextStyle(
color: Color(0xFF648CBA),
decoration: TextDecoration.underline,
fontWeight: FontWeight
.bold, // Makes the text bold
decorationColor: Color(0xFF648CBA),
decorationThickness: 1,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// Add action for Privacy Policy tap
},
),
TextSpan(
text: ' of FCSC.',
style: TextStyle(color: Colors.black),
),
],
),
textAlign: TextAlign.start,
maxLines: 2,
overflow: TextOverflow.visible,
softWrap: true,
),
],
),
),
], ],
), ),
SizedBox(height: 20), if (showError)
Center( Row(
child: GestureDetector( mainAxisAlignment: MainAxisAlignment.start,
onTap: () { children: [
final userId = widget.userId; Padding(
final email = _emailController.text; padding: const EdgeInsets.only(left: 10.0),
child: Text(
'Please agree to terms and conditions',
style: TextStyle(
color: Color(0xFFb22222),
fontSize: 12,
),
),
),
],
),
SizedBox(height: 20),
Center(
child: GestureDetector(
onTap: () {
final userId = widget.userId;
final email = _emailController.text;
context.go('/createNewPw/$userId/$email'); context.go('/createNewPw/$userId/$email');
},
child: Text(
'Change Password',
style: TextStyle(
color: Color(0xFF648CBA),
fontSize: 14,
fontWeight: FontWeight.w700,
fontFamily: 'Roboto',
// decoration: TextDecoration.underline),
),
),
),
),
SizedBox(height: 20),
// ElevatedButton.icon(
ElevatedButton(
onPressed: () {
if ((_formKey.currentState?.validate() ?? false) &&
(isChecked)) {
_formKey.currentState?.save();
showConfirmationDialog(context);
} else {
setState(() {
showError =
!isChecked; // Show error if the checkbox is not checked
});
}
}, },
child: Text( style: ElevatedButton.styleFrom(
"Change Password", backgroundColor: Color(0xFF92722A),
style: TextStyle( minimumSize: Size(double.infinity, 50),
color: Colors.blue, shape: RoundedRectangleBorder(
decoration: TextDecoration.underline), borderRadius: BorderRadius.circular(8),
),
), ),
), child: Row(
), mainAxisAlignment:
SizedBox(height: 20), MainAxisAlignment.center, // Center the content
ElevatedButton.icon( children: [
onPressed: () { Text(
if ((_formKey.currentState?.validate() ?? false) && 'Save',
(isChecked)) { style: TextStyle(color: Colors.white),
_formKey.currentState?.save(); ),
showConfirmationDialog(context); SizedBox(width: 8), // Add space between text and icon
} else { Image.asset(
setState(() { MiscIconAssetPath.save,
showError = color: Colors.white,
!isChecked; // Show error if the checkbox is not checked width: 20,
}); height: 20,
} ),
// if ((_formKey.currentState?.validate() ?? false) && (isChecked)) { ],
// _formKey.currentState?.save();
// showConfirmationDialog(context);
// // if (isChecked) {
// // _formKey.currentState?.save();
// // // _confirmSaveProfile();
// // showConfirmationDialog(context);
// // }
// else {
// setState(() {
// showError = !isChecked; // Show error if the checkbox is not checked
// });
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Please accept the terms and conditions.',style: TextStyle(color: Colors.white),)),
// );
//}
//}
},
icon: Icon(
Icons.save,
color: Colors.white,
),
label: Text(
'Save',
style: TextStyle(color: Colors.white),
),
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF92722A),
minimumSize: Size(double.infinity, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
), ),
), )
), ],
], ),
), ),
), ),
), ),
), ),
//bottomNavigationBar: MyBottomNavBar(),
); );
} }
} }

View File

@ -25,13 +25,13 @@ class ThemedFormField extends HookWidget {
final isObscured = useState<bool>(true); final isObscured = useState<bool>(true);
final boxDecoration = BoxDecoration( final boxDecoration = BoxDecoration(
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
boxShadow: [ // boxShadow: [
BoxShadow( // BoxShadow(
offset: const Offset(0, 1), // offset: const Offset(0, 1),
blurRadius: 2, // blurRadius: 2,
color: Colors.black.withOpacity(0.1), // color: Colors.black.withOpacity(0.1),
), // ),
], // ],
); );
final obscureBtn = IconButton( final obscureBtn = IconButton(
onPressed: () => isObscured.value = !isObscured.value, onPressed: () => isObscured.value = !isObscured.value,

View File

@ -1,5 +1,6 @@
import 'package:external_repos/external_repos.dart'; import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -16,8 +17,9 @@ import 'package:uae_stat/presentation/components/lang_toggle.dart';
import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart'; import 'package:uae_stat/presentation/components/my_bottom_nav_bar.dart';
import 'package:uae_stat/presentation/components/space.dart'; import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/components/themed_text_field.dart'; import 'package:uae_stat/presentation/components/themed_text_field.dart';
import 'package:uae_stat/presentation/routes/auth_routes/pocketbase_service.dart';
import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart'; import 'package:uae_stat/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart';
import 'package:http/http.dart' as http;
import '../../Screens/auth_verification/registration.dart'; import '../../Screens/auth_verification/registration.dart';
import 'package:pocketbase/pocketbase.dart'; import 'package:pocketbase/pocketbase.dart';
@ -26,7 +28,10 @@ import '../../components/my_toggle.dart';
class LoginRoute extends HookConsumerWidget { class LoginRoute extends HookConsumerWidget {
final pb = PocketBase('https://pb.venbait.in'); final pb = PocketBase('https://pb.venbait.in');
final _pbService = PocketBaseService();
// final _pb = PocketBase('http://127.0.0.1:8090'); // final _pb = PocketBase('http://127.0.0.1:8090');
// final bool isLoginScreen; // Pass `true` if this is the login screen
// LoginRoute({Key? key, required this.isLoginScreen}) : super(key: key);
LoginRoute({super.key}); LoginRoute({super.key});
dynamic userData; dynamic userData;
String? role; String? role;
@ -93,52 +98,91 @@ class LoginRoute extends HookConsumerWidget {
final forgotPwFormKey = GlobalKey<FormState>(); final forgotPwFormKey = GlobalKey<FormState>();
final email = await showDialog<String>( final email = await showDialog<String>(
context: context, context: context,
builder: (dialogCtx) => AlertDialog( builder: (dialogCtx) => SizedBox(
title: Text( // width: 850, // Increase dialog width
context.translate( child: AlertDialog(
'Reset Password', shape: RoundedRectangleBorder(
'إعادة تعيين كلمة المرور', borderRadius: BorderRadius.circular(20), // Rounded corners
), ),
elevation: 10,
shadowColor: Colors.black12,
title: Text(
context.translate(
'Reset Password',
'إعادة تعيين كلمة المرور',
),
style: TextStyle(
fontSize: 20,
color: Color(0xFF898C81),
fontWeight: FontWeight.w600),
),
content: Form(
key: forgotPwFormKey,
child: TextFormField(
validator: FieldValidator.email(),
controller: emailCtl,
decoration: InputDecoration(
labelText: context.translate(
'Email address',
'عنوان البريد الإلكتروني',
),
labelStyle: TextStyle(
fontSize: 16,
color: Color(0xFF898C81), // For label text color
// fontWeight: FontWeight.w600,
),
),
),
),
actions: [
TextButton(
onPressed: Navigator.of(dialogCtx, rootNavigator: true).pop,
style: TextButton.styleFrom(
// backgroundColor: Colors.white, // Background color
foregroundColor: Color(0xFF92722A), // Font (text) color
side: BorderSide(
color: Color(0xFF92722A)), // Border outline color
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(8), // Optional: Rounded corners
),
padding: EdgeInsets.symmetric(
horizontal: 16, vertical: 12), // Optional: Padding
),
child: Text(
context.translate(
'Return',
'يعود',
),
),
),
ElevatedButton(
onPressed: () {
final isValid = forgotPwFormKey.currentState!.validate();
if (!isValid) return;
Navigator.of(dialogCtx, rootNavigator: true).pop(
emailCtl.text,
);
},
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF92722A),
foregroundColor: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 15, vertical: 15), // Optional: Adjusts size
shape: RoundedRectangleBorder(
// Optional: Adds rounded corners
borderRadius: BorderRadius.circular(12),
),
),
child: Text(
context.translate(
'Send Reset Email',
'إرسال إعادة تعيين البريد الإلكتروني',
),
),
),
],
), ),
content: Form(
key: forgotPwFormKey,
child: TextFormField(
validator: FieldValidator.email(),
controller: emailCtl,
decoration: InputDecoration(
labelText: context.translate(
'Email address',
'عنوان البريد الإلكتروني',
),
),
),
),
actions: [
TextButton(
onPressed: Navigator.of(dialogCtx, rootNavigator: true).pop,
child: Text(
context.translate(
'Return',
'يعود',
),
),
),
ElevatedButton(
onPressed: () {
final isValid = forgotPwFormKey.currentState!.validate();
if (!isValid) return;
Navigator.of(dialogCtx, rootNavigator: true).pop(
emailCtl.text,
);
},
child: Text(
context.translate(
'Send Reset Email',
'إرسال إعادة تعيين البريد الإلكتروني',
),
),
),
],
), ),
); );
if (email == null) return; if (email == null) return;
@ -174,7 +218,7 @@ class LoginRoute extends HookConsumerWidget {
), ),
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 14, fontSize: 14,
color: MyTheme.topicColor(IndicatorTopic.economy).shade600, color: Color(0xFF985400),
), ),
), ),
); );
@ -253,6 +297,22 @@ class LoginRoute extends HookConsumerWidget {
if (!context.mounted || session == null) return; if (!context.mounted || session == null) return;
final userId = session.id; final userId = session.id;
if (userId.isNotEmpty) { if (userId.isNotEmpty) {
final url =
Uri.parse("https://pb.venbait.in/api/login_success?id=$userId");
try {
final response = await http.get(url);
if (response.statusCode == 200) {
print("Login success API call successful: ${response.body}");
} else {
print(
"Failed to call login success API. Status code: ${response.statusCode}");
}
} catch (e) {
print("Error calling login success API: $e");
}
await saveUserId(userId); await saveUserId(userId);
} }
try { try {
@ -340,7 +400,10 @@ class LoginRoute extends HookConsumerWidget {
), ),
), ),
6.horizontalSpace, 6.horizontalSpace,
const Icon(Icons.chevron_right_outlined), const Icon(
Icons.chevron_right_outlined,
color: Colors.white, // Set your desired color here
)
], ],
), ),
), ),
@ -354,14 +417,21 @@ class LoginRoute extends HookConsumerWidget {
'Email', 'Email',
'بريد إلكتروني', 'بريد إلكتروني',
), ),
validator: FieldValidator.email(), validator: (value) {
if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب');
}
return FieldValidator.email()(value);
},
imgPath: MiscIconAssetPath.person, imgPath: MiscIconAssetPath.person,
controller: emailCtl, controller: emailCtl,
), ),
15.verticalSpace, 15.verticalSpace,
ThemedFormField( ThemedFormField(
validator: (text) { 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 'The password must be at least 8 characters';
} }
return FieldValidator.password(minLength: 8)(text); return FieldValidator.password(minLength: 8)(text);
@ -375,9 +445,12 @@ class LoginRoute extends HookConsumerWidget {
isObscurable: true, isObscurable: true,
), ),
// 6.verticalSpace, // 6.verticalSpace,
Align( Padding(
alignment: AlignmentDirectional.topEnd, padding: EdgeInsets.zero,
child: forgotPwBtn, child: Align(
alignment: AlignmentDirectional.topEnd,
child: forgotPwBtn,
),
), ),
10.verticalSpace, 10.verticalSpace,
loginBtn, loginBtn,
@ -403,7 +476,7 @@ class LoginRoute extends HookConsumerWidget {
10.verticalSpace, 10.verticalSpace,
Text( Text(
context.translate( context.translate(
'Please login to access UAEs key official statistics', 'Please login to access \n UAEs key official statistics',
'يرجى تسجيل الدخول للوصول إلى الإحصاءات الرسمية الرئيسية لدولة الإمارات العربية المتحدة', 'يرجى تسجيل الدخول للوصول إلى الإحصاءات الرسمية الرئيسية لدولة الإمارات العربية المتحدة',
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
@ -412,9 +485,9 @@ class LoginRoute extends HookConsumerWidget {
'Roboto', 'Roboto',
'NotoKufi', 'NotoKufi',
), ),
fontSize: 18, fontSize: context.translate(18.0, 14.0),
color: const Color(0xff898C81), color: const Color(0xff898C81),
fontWeight: FontWeight.w700, fontWeight: FontWeight.w600,
), ),
), ),
], ],
@ -429,7 +502,7 @@ class LoginRoute extends HookConsumerWidget {
'Roboto', 'Roboto',
'NotoKufi', 'NotoKufi',
), ),
fontSize: 18, fontSize: 14,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
children: [ children: [
@ -451,63 +524,71 @@ class LoginRoute extends HookConsumerWidget {
'سجل الان', 'سجل الان',
), ),
style: TextStyle( style: TextStyle(
color: MyTheme.topicColor(IndicatorTopic.economy).shade600, color: Color(0xFF985400),
), ),
), ),
], ],
), ),
), ),
); );
final continueAsGuestBtn = SizedBox( // final continueAsGuestBtn = SizedBox(
width: double.infinity, // width: double.infinity,
child: ElevatedButton( // child: ElevatedButton(
onPressed: () => // onPressed: () async {
context.go('/myhomepage'), // final prefs = await SharedPreferences.getInstance();
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'), // prefs.clear();
style: ButtonStyle( // final userId = 'guest';
shape: WidgetStatePropertyAll( // if (userId.isNotEmpty) {
RoundedRectangleBorder( // await saveUserId(userId);
borderRadius: BorderRadius.circular(10), // }
), // context.go('/myhomepage');
), // //context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
padding: const WidgetStatePropertyAll( // },
EdgeInsets.symmetric(vertical: 10.5), // //context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
), // style: ButtonStyle(
textStyle: WidgetStatePropertyAll( // shape: WidgetStatePropertyAll(
TextStyle( // RoundedRectangleBorder(
fontFamily: context.translate( // borderRadius: BorderRadius.circular(10),
'Roboto', // ),
'NotoKufi', // ),
), // padding: const WidgetStatePropertyAll(
fontSize: 16, // EdgeInsets.symmetric(vertical: 10.5),
fontWeight: FontWeight.w600, // ),
), // textStyle: WidgetStatePropertyAll(
), // TextStyle(
backgroundColor: WidgetStatePropertyAll( // fontFamily: context.translate(
MyTheme.topicColor(IndicatorTopic.environment), // 'Roboto',
), // 'NotoKufi',
foregroundColor: const WidgetStatePropertyAll( // ),
Colors.white, // fontSize: 16,
), // fontWeight: FontWeight.w600,
), // ),
child: Row( // ),
mainAxisAlignment: MainAxisAlignment.center, // backgroundColor: WidgetStatePropertyAll(
children: [ // MyTheme.topicColor(IndicatorTopic.environment),
Text( // ),
context.translate( // foregroundColor: const WidgetStatePropertyAll(
'Continue as Guest', // Colors.white,
'استمر كضيف', // ),
), // ),
), // child: Row(
6.horizontalSpace, // mainAxisAlignment: MainAxisAlignment.center,
const Icon(Icons.chevron_right_outlined), // children: [
], // Text(
), // context.translate(
), // 'Continue as Guest',
); // 'استمر كضيف',
// ),
// ),
// 6.horizontalSpace,
// const Icon(Icons.chevron_right_outlined),
// ],
// ),
// ),
// );
final fcscBanner = Image.asset( final fcscBanner = Image.asset(
BannerAssetPath.fcsc, BannerAssetPath.fcsc,
height: 56, height: 40,
); );
final screenWidth = MediaQuery.of(context).size.width; final screenWidth = MediaQuery.of(context).size.width;
final listViewHorizontalPadding = final listViewHorizontalPadding =
@ -520,31 +601,64 @@ class LoginRoute extends HookConsumerWidget {
36.verticalSpace, 36.verticalSpace,
Align( Align(
alignment: AlignmentDirectional.topEnd, alignment: AlignmentDirectional.topEnd,
child: MyToggle(isOn: locale?.languageCode == 'en', child: MyToggle(
isOn: locale?.languageCode == 'en',
knobTextWhenOn: 'ع', knobTextWhenOn: 'ع',
knobTextWhenOff: 'EN', knobTextWhenOff: 'EN',
pathColorWhenOn: Colors.grey.shade300, pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300, pathColorWhenOff: Colors.grey.shade300,
onTap: (){ onTap: () {
ref.read(localeProvider.notifier).toggleLocale(); ref.read(localeProvider.notifier).toggleLocale();
},), },
),
), ),
16.verticalSpace, 16.verticalSpace,
helloAndPleaseLoginTexts, helloAndPleaseLoginTexts,
42.verticalSpace, 42.verticalSpace,
form, form,
20.verticalSpace, 15.verticalSpace,
dontHaveAnAccountRegisterBtn, dontHaveAnAccountRegisterBtn,
36.verticalSpace, 20.verticalSpace,
continueAsGuestBtn, // continueAsGuestBtn,
72.verticalSpace, 95.verticalSpace,
fcscBanner, fcscBanner,
], ],
); );
final bgScaffold = Scaffold( // final bgScaffold = Scaffold(
backgroundColor: Colors.white, // backgroundColor: Colors.white,
body: SafeArea(child: scaffoldBody), // body: SafeArea(child: scaffoldBody),
// );
// return bgScaffold;
return PopScope(
canPop: false, // Allow back navigation only if not login screen
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
_showExitConfirmation(context); // Show exit confirmation dialog
},
child: Scaffold(
backgroundColor: Colors.white,
body: SafeArea(child: scaffoldBody),
),
);
}
void _showExitConfirmation(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text("Exit App"),
content: Text("Are you sure you want to exit?"),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(), // Close dialog
child: Text("Cancel"),
),
TextButton(
onPressed: () => SystemNavigator.pop(), // Exit the app
child: Text("Exit"),
),
],
),
); );
return bgScaffold;
} }
} }

View File

@ -0,0 +1,66 @@
import 'package:pocketbase/pocketbase.dart';
import 'package:shared_preferences/shared_preferences.dart';
class PocketBaseService {
final PocketBase pb = PocketBase('https://pb.venbait.in');
// Save session to local storage
Future<void> saveSession() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('pb_auth', pb.authStore.token);
await prefs.setString('pb_user', pb.authStore.model.toJson());
await prefs.setInt('pb_auth_time', DateTime.now().millisecondsSinceEpoch);
}
// Load session from local storage
Future<bool> loadSession() async {
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('pb_auth');
final userJson = prefs.getString('pb_user');
if (token != null && userJson != null) {
pb.authStore
.save(token, RecordModel.fromJson(userJson as Map<String, dynamic>));
if (!pb.authStore.isValid || isSessionExpired(prefs)) {
await clearSession();
return false;
}
return true;
}
return false;
}
// Check if the session is expired (4 days)
bool isSessionExpired(SharedPreferences prefs) {
final savedTime = prefs.getInt('pb_auth_time') ?? 0;
final currentTime = DateTime.now().millisecondsSinceEpoch;
const sessionDuration = Duration(days: 4);
return (currentTime - savedTime) > sessionDuration.inMilliseconds;
}
// User login
Future<bool> loginUser(String email, String password) async {
try {
await pb.collection('users').authWithPassword(email, password);
await saveSession();
return true;
} catch (e) {
return false;
}
}
// User logout
Future<void> logoutUser() async {
pb.authStore.clear();
await clearSession();
}
// Clear session from local storage
Future<void> clearSession() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('pb_auth');
await prefs.remove('pb_user');
await prefs.remove('pb_auth_time');
}
}

View File

@ -163,43 +163,26 @@
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import '../../drawer_routes/custom_drawer_routes.dart'; import 'package:pocketbase/pocketbase.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:uae_stat/presentation/components/indicators/locale_provider.dart';
import 'package:tutorial_coach_mark/tutorial_coach_mark.dart';
import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
class MyHomePage extends StatefulWidget { class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({super.key}); const MyHomePage({super.key});
@override @override
State<MyHomePage> createState() => _MyHomePageState(); ConsumerState<MyHomePage> createState() => _MyHomePageState();
} }
void handleInfoCardClick(BuildContext context, String data, Color color) { class _MyHomePageState extends ConsumerState<MyHomePage> {
// Handle navigation and pass dynamic data
print(data);
final dataSets = data;
if (dataSets != null) {
// Perform navigation
context.go('/chartScreen/$dataSets');
} else {
// Show error if dataSet is null
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('No dataset available')),
);
}
// final dataSets = data;
// print(dataSets);
// if (dataSets == 'hotels') {
// context.go('/chartScreen/$dataSets');
// } else if (dataSets == 'divorces') {
// context.go('/chartScreen/$dataSets');
// } else if (dataSets == 'marriages') {
// context.go('/Chart/$dataSets');
// }
}
class _MyHomePageState extends State<MyHomePage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
double myheight = MediaQuery.of(context).size.height; double myheight = MediaQuery.of(context).size.height;
@ -215,28 +198,660 @@ class _MyHomePageState extends State<MyHomePage> {
} }
} }
class EconomyStatsWidget extends StatefulWidget { class EconomyStatsWidget extends ConsumerStatefulWidget {
const EconomyStatsWidget({super.key}); const EconomyStatsWidget({super.key});
@override @override
EconomyStatsState createState() => EconomyStatsState(); ConsumerState createState() => EconomyStatsState();
} }
class EconomyStatsState extends State<EconomyStatsWidget> { class EconomyStatsState extends ConsumerState<EconomyStatsWidget> {
List<dynamic> data = []; List<dynamic> data = [];
final _pb = PocketBase('https://pb.venbait.in');
bool isLoading = true; bool isLoading = true;
final GlobalKey cardTopicKey = GlobalKey();
final GlobalKey cardsKey = GlobalKey();
late TutorialCoachMark tutorialCoachMark;
late List<TargetFocus> homeTargets;
late List<TargetFocus> previousHomeTargets;
void handleSkip() {
tutorialCoachMark.skip();
debugPrint('Skip clicked');
ref.read(chartsTourProvider.notifier).state = true;
ref.read(previousChartsTourProvider.notifier).state = true;
ref.read(homeTourProvider.notifier).state = true;
ref.read(previousHomeTourProvider.notifier).state = true;
ref.read(scaffoldTourProvider.notifier).state = true;
ref.read(previousScaffoldTourProvider.notifier).state = true;
}
//Method waits app tour to render Targets
void _starTourRender() {
if (cardTopicKey.currentContext != null) {
print('render finished');
_showHomeTour();
} else {
Future.delayed(Duration(milliseconds: 100), _starTourRender);
}
}
//Method to Start App Tour
void _showHomeTour() {
final homeTour = ref.watch(homeTourProvider);
final previousHomeTour = ref.watch(previousHomeTourProvider);
// Check and show tutorials
if (!homeTour) {
// Show Home Tour
_initTarget();
tutorialCoachMark = TutorialCoachMark(
paddingFocus: 0,
useSafeArea: true,
pulseEnable: false,
hideSkip: true,
targets: homeTargets,
onFinish: () {
ref.read(homeTourProvider.notifier).state = true;
ref.read(chartsTourProvider.notifier).state = false;
debugPrint('Home Tutorial Finished');
context.go(
'/chartScreen/hotels?bgColor=0xFF90B0D5&mainTopic=ECONOMY&title=Hotel+Establishments');
},
)..show(context: context);
} else if (!previousHomeTour) {
_initPreviousTarget();
tutorialCoachMark = TutorialCoachMark(
paddingFocus: 0,
useSafeArea: true,
pulseEnable: false,
hideSkip: true,
targets: previousHomeTargets,
onFinish: () {
ref.read(previousHomeTourProvider.notifier).state = true;
debugPrint('Previous Home Tutorial Finished');
context.go(
'/chartScreen/hotels?bgColor=0xFF90B0D5&mainTopic=ECONOMY&title=Hotel+Establishments');
},
)..show(context: context);
}
}
void _initTarget() {
final double screenWidth = MediaQuery.of(context).size.width;
homeTargets = [
TargetFocus(
identify: 'cardTopicKey',
keyTarget: cardTopicKey,
shape: ShapeLightFocus.RRect,
radius: 8,
paddingFocus: 16,
contents: [
createTargetContent(
text: AppLocalizations.of(context)!.home_topic,
alignment: ContentAlign.bottom,
gap: 0,
space: 43,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.70,
child: Stack(
children: [
Positioned(
bottom: 20,
left: 16,
right: 16,
child: Column(
children: [
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: const EdgeInsets.only(
right: 10), // Adjust the value as needed
child: Text(
'1/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
),
SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () => handleSkip(),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
child: const Text(
'Skip',
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(
color: Color(0xFF7DAFBC), width: 1),
),
child: IconButton(
padding: EdgeInsets.zero,
iconSize: 20,
icon: const Icon(Icons.arrow_forward,
color: Colors.white),
onPressed: () {
tutorialCoachMark.next();
},
),
),
],
),
],
),
],
)),
],
),
),
),
TargetContent(
padding: EdgeInsets.only(left: screenWidth * 0.4, bottom: 0),
align: ContentAlign.bottom,
child: Container(
width: 200,
height: 77,
child: Stack(
children: [
Image.asset(
'assets/app_tour/right_down.png',
fit: BoxFit.contain,
),
],
),
),
),
],
),
TargetFocus(
identify: 'cardsKey',
keyTarget: cardsKey,
shape: ShapeLightFocus.RRect,
radius: 7,
paddingFocus: 6,
// targetPosition:TargetPosition(),
contents: [
createTargetContent(
text: AppLocalizations.of(context)!.economy,
alignment: ContentAlign.bottom,
gap: 0,
space: 43,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height *
0.5, // Set an appropriate height for the Stack
child: Stack(
children: [
Positioned(
bottom: 25,
left: 16,
right: 16,
child: Column(
children: [
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 10),
child: Text(
'2/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
)),
const SizedBox(height: 3),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () => handleSkip(),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
child: const Text(
'Skip',
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white, width: 2.0),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_back,
color: Colors.white),
onPressed: () {
tutorialCoachMark.previous();
},
),
),
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(
color: Color(0xFF7DAFBC), width: 1),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward,
color: Colors.white),
onPressed: () {
tutorialCoachMark.next();
},
),
),
],
),
],
),
],
),
),
],
),
),
),
TargetContent(
padding: EdgeInsets.only(left: screenWidth * 0.4, bottom: 0),
align: ContentAlign.bottom,
child: Container(
width: 200,
height: 77,
child: Stack(
children: [
Image.asset(
'assets/app_tour/right_down.png',
fit: BoxFit.contain,
),
],
),
),
),
// TargetContent(
// align: ContentAlign.bottom,
// child: SizedBox(
// width: MediaQuery.of(context).size.width ,
// height:MediaQuery.of(context).size.height*0.69 ,
// child: Stack(
// children: [
// Positioned(
// left: MediaQuery.of(context).size.width*0.40,
// top: _imageTopicPosition.dy-29,
// child: Image.asset(
// 'assets/app_tour/right_down.png',
// width: 40,
// height: 90,
// ),
// ),
// ]
// ),
//
// ),
// ),
],
),
];
}
void _initPreviousTarget() {
final double screenWidth = MediaQuery.of(context).size.width;
previousHomeTargets = [
TargetFocus(
identify: 'cardsKey',
keyTarget: cardsKey,
shape: ShapeLightFocus.RRect,
radius: 7,
paddingFocus: 6,
contents: [
createTargetContent(
text: AppLocalizations.of(context)!.economy,
alignment: ContentAlign.bottom,
gap: 0,
space: 43,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height *
0.5, // Set an appropriate height for the Stack
child: Stack(
children: [
Positioned(
bottom: 25,
left: 16,
right: 16,
child: Column(
children: [
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 10),
child: Text(
'2/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
)),
SizedBox(
height: 5,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () => handleSkip(),
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
child: const Text(
'Skip',
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
Row(
children: [
Row(
children: [
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Colors.white, width: 2.0),
),
child: IconButton(
icon: const Icon(Icons.arrow_back,
color: Colors.white),
onPressed: () {
tutorialCoachMark.next();
},
),
),
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(
color: Color(0xFF7DAFBC),
width: 1.5),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward,
color: Colors.white),
onPressed: () {
ref
.read(previousHomeTourProvider
.notifier)
.state = true;
ref
.read(chartsTourProvider.notifier)
.state = false;
context.go(
'/chartScreen/hotels?bgColor=0xFF90B0D5&mainTopic=ECONOMY&title=Hotel+Establishments');
tutorialCoachMark.finish();
},
),
),
],
),
],
),
],
),
],
),
),
],
),
),
),
TargetContent(
padding: EdgeInsets.only(left: screenWidth * 0.4, bottom: 0),
align: ContentAlign.bottom,
child: Container(
width: 200,
height: 77,
child: Stack(
children: [
Image.asset(
'assets/app_tour/right_down.png',
fit: BoxFit.contain,
),
],
),
),
),
],
),
TargetFocus(
identify: 'cardTopicKey',
keyTarget: cardTopicKey,
shape: ShapeLightFocus.RRect,
radius: 8,
paddingFocus: 16,
contents: [
createTargetContent(
text: AppLocalizations.of(context)!.home_topic,
alignment: ContentAlign.bottom,
gap: 0,
space: 43,
),
TargetContent(
align: ContentAlign.bottom,
child: SizedBox(
width: double.infinity,
height: MediaQuery.of(context).size.height * 0.69,
child: Stack(
children: [
Positioned(
bottom: 20,
left: 16,
right: 16,
child: Column(
children: [
Align(
alignment: Alignment.bottomRight,
child: Padding(
padding: EdgeInsets.only(right: 10),
child: Text(
'1/7',
style: const TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton(
onPressed: () {
tutorialCoachMark.skip();
debugPrint('Skip clicked');
},
style: ElevatedButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: Colors.white),
backgroundColor: Colors.transparent,
elevation: 0,
),
child: const Text(
'Skip',
style: TextStyle(
color: Colors.white,
fontSize: 14,
),
),
),
Row(
children: [
const SizedBox(width: 10),
Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Color(0xFF7DAFBC),
border: Border.all(
color: Color(0xFF7DAFBC), width: 1.5),
),
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.arrow_forward,
color: Colors.white),
onPressed: () {
tutorialCoachMark.previous();
},
),
),
],
),
],
),
],
),
),
],
),
),
),
TargetContent(
padding: EdgeInsets.only(left: screenWidth * 0.4, bottom: 0),
align: ContentAlign.bottom,
child: Container(
width: 200,
height: 77,
child: Stack(
children: [
Image.asset(
'assets/app_tour/right_down.png',
fit: BoxFit.contain,
),
],
),
),
),
],
),
];
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
fetchData(); final locale = ref.read(localeProvider);
fetchData(locale?.languageCode ?? 'en');
_fetchUserData();
} }
Future<void> fetchData() async { // @override
// void didChangeDependencies() {
// super.didChangeDependencies();
// // Access the provider here
// final locale = ref.watch(localeProvider);
// fetchData(locale); // Pass the locale to the fetchData method
// }
Future<String?> getUserId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString('userId'); // Retrieve the userId
}
Future<void> _fetchUserData() async {
try {
final userId = await getUserId();
print('EDIT PROFILE isPageLoad');
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',
},
);
final loginCount = userDetailsResponse.data['login_count'];
print(loginCount);
if (loginCount == 1) {
setState(() {
WidgetsBinding.instance.addPostFrameCallback((_) {
_starTourRender();
});
});
}
} catch (e) {
print('Error fetching user details: $e');
}
}
Future<void> fetchData(locale) async {
const baseUrl = 'https://pb.venbait.in/api/getHomePageData'; const baseUrl = 'https://pb.venbait.in/api/getHomePageData';
try { try {
final response = await http.get(Uri.parse(baseUrl)); final response = await http.get(Uri.parse(baseUrl + '?language=$locale'));
if (response.statusCode == 200) { if (response.statusCode == 200) {
setState(() { setState(() {
data = json.decode(response.body); data = json.decode(response.body);
@ -283,6 +898,10 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
} }
Widget build(BuildContext context) { Widget build(BuildContext context) {
ref.listen<Locale?>(localeProvider, (previous, next) {
final localeCode = next?.languageCode ?? 'en'; // Default to 'en' if null
fetchData(localeCode);
});
double myheight = MediaQuery.of(context).size.height; double myheight = MediaQuery.of(context).size.height;
double mywidth = MediaQuery.of(context).size.width; double mywidth = MediaQuery.of(context).size.width;
@ -336,6 +955,9 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
RoundedCornerContainer( RoundedCornerContainer(
key: mainTopic['main_topic'] == 'ECONOMY'
? cardTopicKey
: null,
text: mainTopic['main_topic'], text: mainTopic['main_topic'],
backgroundColor: backgroundColor, backgroundColor: backgroundColor,
textStyle: const TextStyle( textStyle: const TextStyle(
@ -347,6 +969,9 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
SizedBox( SizedBox(
height: myheight / 4.8, // Set dynamic height height: myheight / 4.8, // Set dynamic height
child: Container( child: Container(
key: mainTopic['main_topic'] == 'ECONOMY'
? cardsKey
: null,
// color: Colors.grey[200], // Set a background color for the scrollable container // color: Colors.grey[200], // Set a background color for the scrollable container
child: ConstrainedBox( child: ConstrainedBox(
constraints: BoxConstraints( constraints: BoxConstraints(
@ -386,7 +1011,8 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
children: _buildRows( children: _buildRows(
[tileData], [tileData],
borderColor, borderColor,
mainTopic['color_pattern']), mainTopic['color_pattern'],
mainTopic['main_topic']),
), ),
), ),
], ],
@ -452,8 +1078,8 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
// return rows; // return rows;
// } // }
List<Widget> _buildRows( List<Widget> _buildRows(List<Map<String, dynamic>> tileData, Color borderColor,
List<Map<String, dynamic>> tileData, Color borderColor, color_pattern) { String colorPattern, String mainTopic) {
// tileData.sort((a, b) => // tileData.sort((a, b) =>
// (a['data_set_list_order'] ?? 0).compareTo(b['data_set_list_order'] ?? 0),); // (a['data_set_list_order'] ?? 0).compareTo(b['data_set_list_order'] ?? 0),);
@ -479,8 +1105,10 @@ List<Widget> _buildRows(
dataset: tile['data_set']!, dataset: tile['data_set']!,
bordercolor: borderColor, bordercolor: borderColor,
textcolor: borderColor, textcolor: borderColor,
colorPattern: colorPattern,
backgroundColor: backgroundColor:
Colors.white, // Set background color for InfoCard Colors.white, // Set background color for InfoCard
mainTopic: mainTopic,
onTap: () {}, onTap: () {},
), ),
); );
@ -529,10 +1157,12 @@ class RoundedCornerContainer extends StatelessWidget {
class InfoCard extends StatelessWidget { class InfoCard extends StatelessWidget {
final String title; final String title;
final String mainTopic;
final String subtitle; final String subtitle;
final String value; final String value;
final String dataset; final String dataset;
final Color bordercolor; final Color bordercolor;
final String colorPattern;
final Color? textcolor; final Color? textcolor;
final VoidCallback onTap; final VoidCallback onTap;
final Color backgroundColor; final Color backgroundColor;
@ -544,9 +1174,11 @@ class InfoCard extends StatelessWidget {
required this.value, required this.value,
required this.dataset, required this.dataset,
required this.bordercolor, required this.bordercolor,
required this.colorPattern,
this.textcolor, this.textcolor,
required this.onTap, required this.onTap,
required this.backgroundColor, required this.backgroundColor,
required this.mainTopic,
}) : super(key: key); }) : super(key: key);
@override @override
@ -554,11 +1186,15 @@ class InfoCard extends StatelessWidget {
double myheight = MediaQuery.of(context).size.height; double myheight = MediaQuery.of(context).size.height;
double mywidth = MediaQuery.of(context).size.width; double mywidth = MediaQuery.of(context).size.width;
print("colorPatternInfo -$colorPattern $mainTopic");
final encodedMainTopic = Uri.encodeComponent(mainTopic);
final encodedTitle = Uri.encodeComponent(title);
final encodedKey = Uri.encodeQueryComponent('home');
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
// Call handleInfoCardClick and pass the title and color context.go(
handleInfoCardClick( '/chartScreen/$dataset?bgColor=$colorPattern&mainTopic=$encodedMainTopic&title=$encodedTitle&key=$encodedKey');
context, dataset, bordercolor); // Pass 'title' to the function
}, },
child: Container( child: Container(
height: myheight / 12, height: myheight / 12,
@ -589,6 +1225,7 @@ class InfoCard extends StatelessWidget {
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
// color: Colors.black, // color: Colors.black,
fontFamily: 'Roboto',
color: Color(0xFF000000), color: Color(0xFF000000),
), ),
), ),
@ -608,6 +1245,7 @@ class InfoCard extends StatelessWidget {
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: const TextStyle( style: const TextStyle(
fontSize: 11, fontSize: 11,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400, fontWeight: FontWeight.w400,
color: Color(0xFF8E8E8E), color: Color(0xFF8E8E8E),
), ),
@ -626,8 +1264,9 @@ class InfoCard extends StatelessWidget {
Text( Text(
value, value,
style: TextStyle( style: TextStyle(
fontSize: 26, fontSize: 18,
fontWeight: FontWeight.w900, fontWeight: FontWeight.w900,
fontFamily: 'Roboto',
color: textcolor ?? Colors.black, color: textcolor ?? Colors.black,
), ),
), ),

View File

@ -0,0 +1,194 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
class BookMark extends ConsumerStatefulWidget {
const BookMark({super.key});
@override
ConsumerState<BookMark> createState() => _BookMarkState();
}
class _BookMarkState extends ConsumerState<BookMark> {
// Example dynamic data
final List<Map<String, dynamic>> dataList = [
{
'title': 'GDP(Constant)',
'subtitle': '(2022)(AED)',
'value': '1.62T',
'valueColor': Color(0xFF80A8CD),
'isBookmark': true
},
{
'title': 'Trade Value',
'subtitle': '(Jan-Jan 2024)-AED',
'value': '215.4B',
'valueColor': Color(0xFF80A8CD),
'isBookmark': true
},
{
'title': 'Electricity Production',
'subtitle': '(2022) (GWh)',
'value': '155,438',
'valueColor': Color(0xFFAA8E83),
'isBookmark': true
},
{
'title': 'Crude Oil Production',
'subtitle': '(2022) (1000 b/d)',
'value': '3,064',
'valueColor': Color(0xFF80A8CD),
'isBookmark': true
},
{
'title': 'Quantitiy of Export Oil',
'subtitle': '(2022) (1000 b/d)',
'value': '2717',
'valueColor': Color(0xFF7DAFBC),
'isBookmark': true
},
{
'title': 'Desalinated Water Production',
'subtitle': '(2022) (MCM)',
'value': '1823.8',
'valueColor': Color(0xFF7DAFBC),
'isBookmark': true
},
];
@override
Widget build(BuildContext context) {
final filteredList = dataList.where((item) => item['isBookmark'] == true)
.toList();
return BaseScaffold(
title: Text('Bookmark'),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, // Two columns
crossAxisSpacing: 10.0,
mainAxisSpacing: 10.0,
mainAxisExtent: 100,
),
itemCount: filteredList.length,
itemBuilder: (context, index) {
return _buildBox(filteredList[index], context);
},
),
),
);
}
// Widget for creating a box
Widget _buildBox(Map<String, dynamic> data, BuildContext context) {
return GestureDetector(
onTap: () {
debugPrint("Box clicked: ${data['title']}");
},
child: Container(
width: MediaQuery
.of(context)
.size
.width * 0.4,
height: 100,
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: data['valueColor']),
),
child: Stack(
children: [
Align(
alignment: Alignment.center,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Align(
alignment: Alignment.center,
child: Text(
data['title'] ?? '',
style: TextStyle(
fontWeight: FontWeight.w500,
),
overflow: TextOverflow.ellipsis,
// Truncate long text
maxLines: 1, // Limit to 1 line
),
),
),
GestureDetector(
onTap: () {
debugPrint("Icon clicked in: ${data['title']}");
setState(() {
// Change isOpen to false when icon is clicked
int index = dataList.indexWhere(
(item) => item['title'] == data['title'],);
if (index != -1) {
dataList[index]['isBookmark'] = false;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Removed From Bookmark',
style: TextStyle(color: Color(0xFF2F692C),
fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFFD6E9C6),
duration: Duration(seconds: 2),
),
);
} else {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Failed to Remove',
style: TextStyle(color: Color(0xFFEB5F24),
fontWeight: FontWeight.w600),
),
backgroundColor: Color(0xFF544C4C),
duration: Duration(seconds: 2),
),
);
}
});
},
child: Icon(
Icons.bookmarks_rounded,
color: Colors.black,
size: 20,
),
),
],
),
Text(
data['subtitle'] ?? '',
style: TextStyle(
fontWeight: FontWeight.w500, color: Colors.grey),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
Text(
data['value'] ?? '',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: data['valueColor'],
),
),
],
),
),
],
),
),
);
}
}

View File

@ -28,7 +28,7 @@ class FeedbackForm extends StatefulWidget {
class _FeedbackFormState extends State<FeedbackForm> class _FeedbackFormState extends State<FeedbackForm>
with WidgetsBindingObserver { with WidgetsBindingObserver {
final _pb = final _pb =
PocketBase('https://pb.venbait.in'); // Initialize PocketBase client PocketBase('https://pb.venbait.in'); // Initialize PocketBase client
// final _pb = // final _pb =
// PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client // PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client
@ -46,32 +46,32 @@ class _FeedbackFormState extends State<FeedbackForm>
dynamic userId; dynamic userId;
List<Map<String, dynamic>> get _emojiOptions => [ List<Map<String, dynamic>> get _emojiOptions => [
{ {
"icon": Icons.sentiment_very_dissatisfied, "icon": Icons.sentiment_very_dissatisfied,
"label": context.translate("Terrible", "رهيب"), // Translate here "label": context.translate("Terrible", "سئ جدا"), // Translate here
"value": 1, "value": 1,
}, },
{ {
"icon": Icons.sentiment_dissatisfied, "icon": Icons.sentiment_dissatisfied,
"label": context.translate("Bad", "سيء"), // Translate here "label": context.translate("Bad", "سيء"), // Translate here
"value": 2, "value": 2,
}, },
{ {
"icon": Icons.sentiment_neutral, "icon": Icons.sentiment_neutral,
"label": context.translate("Okay", "تمام"), // Translate here "label": context.translate("Okay", "مقبول"), // Translate here
"value": 3, "value": 3,
}, },
{ {
"icon": Icons.sentiment_satisfied, "icon": Icons.sentiment_satisfied,
"label": context.translate("Good", "جيد"), // Translate here "label": context.translate("Good", "جيد"), // Translate here
"value": 4, "value": 4,
}, },
{ {
"icon": Icons.sentiment_very_satisfied, "icon": Icons.sentiment_very_satisfied,
"label": context.translate("Amazing", "مدهش"), // Translate here "label": context.translate("Amazing", "مذهل"), // Translate here
"value": 5, "value": 5,
}, },
]; ];
static const int _characterLimit = 1200; static const int _characterLimit = 1200;
final RegExp _allowedCharacters = RegExp(r'^[a-zA-Z0-9 .,!?-]*$'); final RegExp _allowedCharacters = RegExp(r'^[a-zA-Z0-9 .,!?-]*$');
@ -213,7 +213,7 @@ class _FeedbackFormState extends State<FeedbackForm>
title: Text(AppLocalizations.of(context)!.feedback_title), title: Text(AppLocalizations.of(context)!.feedback_title),
// appBar: AppBar( // appBar: AppBar(
// backgroundColor: const Color(0xFFf8f9ff), // backgroundColor: const Color(0xFFf8f9ff),
// title: Text(context.translate('Feedback Form', 'نموذج الملاحظات')), // title: Text(AppLocalizations.of(context)!.feedback_title),
// actions: [ // actions: [
// Align( // Align(
// alignment: AlignmentDirectional.topEnd, // alignment: AlignmentDirectional.topEnd,
@ -235,8 +235,8 @@ class _FeedbackFormState extends State<FeedbackForm>
child: _isFeedbackSubmitted child: _isFeedbackSubmitted
? _buildThankYouMessage() ? _buildThankYouMessage()
: _isFeedbackFailed : _isFeedbackFailed
? _buildFailureMessage() ? _buildFailureMessage()
: _buildFeedbackForm(), : _buildFeedbackForm(),
), ),
), ),
); );
@ -249,7 +249,7 @@ class _FeedbackFormState extends State<FeedbackForm>
Text( Text(
context.translate( context.translate(
'Do you have a suggestion or had any problem? Let us know.', 'Do you have a suggestion or had any problem? Let us know.',
'هل لديك اقتراح أو واجهت أي مشكلة؟ اسمحوا لنا أن نعرف.', 'هل لديك اقتراح أو واجهت أي مشكلة؟ أخبرنا',
), ),
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
@ -309,7 +309,7 @@ class _FeedbackFormState extends State<FeedbackForm>
const SizedBox(height: 30), const SizedBox(height: 30),
Text( Text(
context.translate('How good did we do in these aspects?', context.translate('How good did we do in these aspects?',
'ما مدى جودة ما قمنا به في هذه الجوانب؟'), 'ما مدى جودة أدائنا في هذه الجوانب؟'),
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontFamily: 'Roboto', fontFamily: 'Roboto',
@ -321,34 +321,34 @@ class _FeedbackFormState extends State<FeedbackForm>
_buildRatingRow( _buildRatingRow(
context.translate('Ease of use', 'سهولة الاستخدام'), context.translate('Ease of use', 'سهولة الاستخدام'),
_easeOfUseRating, _easeOfUseRating,
(rating) { (rating) {
setState(() { setState(() {
_easeOfUseRating = rating; _easeOfUseRating = rating;
}); });
}, },
), ),
_buildRatingRow( _buildRatingRow(
context.translate('Quality', 'جودة'), context.translate('Quality', 'الجودة'),
_qualityRating, _qualityRating,
(rating) { (rating) {
setState(() { setState(() {
_qualityRating = rating; _qualityRating = rating;
}); });
}, },
), ),
_buildRatingRow( _buildRatingRow(
context.translate('Design', 'تصميم'), context.translate('Design', 'التصميم'),
_designRating, _designRating,
(rating) { (rating) {
setState(() { setState(() {
_designRating = rating; _designRating = rating;
}); });
}, },
), ),
_buildRatingRow( _buildRatingRow(
context.translate('Redundant', 'متكرر'), context.translate('Redundant', 'مكرر'),
_redundancyRating, _redundancyRating,
(rating) { (rating) {
setState(() { setState(() {
_redundancyRating = rating; _redundancyRating = rating;
}); });
@ -362,14 +362,14 @@ class _FeedbackFormState extends State<FeedbackForm>
maxLength: _characterLimit, maxLength: _characterLimit,
decoration: InputDecoration( decoration: InputDecoration(
hintText: context.translate( hintText: context.translate(
'Tell us how we can improve', 'أخبرنا كيف يمكننا تحسين'), 'Tell us how we can improve', 'أخبرنا كيف يمكننا التحسين'),
border: OutlineInputBorder( border: OutlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: _hasError ? Colors.red : Color(0xFF7296BE), color: _hasError ? Colors.red : Color(0xFF7296BE),
), ),
), ),
errorText: errorText:
_hasError ? _errorMessage : null, // Show error below field _hasError ? _errorMessage : null, // Show error below field
), ),
onChanged: (text) { onChanged: (text) {
// Trigger re-validation and character limit on each change // Trigger re-validation and character limit on each change
@ -412,7 +412,7 @@ class _FeedbackFormState extends State<FeedbackForm>
Text( Text(
context.translate( context.translate(
'Submit', 'Submit',
'يُقدِّم', 'إرسال',
), ),
), ),
6.horizontalSpace, 6.horizontalSpace,
@ -514,10 +514,10 @@ class _FeedbackFormState extends State<FeedbackForm>
} }
Widget _buildRatingRow( Widget _buildRatingRow(
String label, String label,
double rating, double rating,
Function(double) onRatingUpdate, Function(double) onRatingUpdate,
) { ) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
@ -573,7 +573,7 @@ class _FeedbackFormState extends State<FeedbackForm>
"design": _designRating, "design": _designRating,
"redundancy": _redundancyRating, "redundancy": _redundancyRating,
"emoji_rating": _emojiOptions[_selectedEmojiIndex!] "emoji_rating": _emojiOptions[_selectedEmojiIndex!]
["label"], // Emoji rating ["label"], // Emoji rating
"feedback": _feedbackController.text, "feedback": _feedbackController.text,
}; };
@ -783,7 +783,7 @@ class LangToggle extends ConsumerWidget {
ref.read(preferencesUseCaseProvider.notifier).updatePreferences( ref.read(preferencesUseCaseProvider.notifier).updatePreferences(
(prefs) => prefs.copyWith(language: languageAfter), (prefs) => prefs.copyWith(language: languageAfter),
); );
await onLoadFeedback(); // Reload feedback text after toggling language await onLoadFeedback(); // Reload feedback text after toggling language
}, },

View File

@ -1,7 +1,12 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import '../../../../components/indicators/locale_provider.dart';
import '../../../../components/my_toggle.dart';
class FAQPage extends StatefulWidget { class FAQPage extends StatefulWidget {
const FAQPage({super.key}); const FAQPage({super.key});
@ -12,63 +17,90 @@ class FAQPage extends StatefulWidget {
class _FAQPageState extends State<FAQPage> { class _FAQPageState extends State<FAQPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BaseScaffold( return Scaffold(
title: Text('FAQs', appBar: AppBar(
style: TextStyle( elevation: 3,
color:Color(0xFF7296BE), shadowColor: Colors.black,
fontWeight: FontWeight.w500, title: Text('Key Features',),
),), leading: IconButton(
showBackButton: true, onPressed: (){
body: SafeArea(child:Column( context.go('/user-guide');
crossAxisAlignment: CrossAxisAlignment.start, },
children: [ color: Colors.black,
Container( icon: const Icon(Icons.arrow_back_ios_new),),
padding: const EdgeInsets.all(16.0), actions: [
width: MediaQuery.of(context).size.width, Consumer(
child: Text.rich( builder: (context, ref, _) {
TextSpan( final locale = ref.watch(localeProvider); // Current locale
text: 'Here are some common questions about the ', return MyToggle(
style: TextStyle( isOn: locale?.languageCode == 'en',
fontSize: 19, knobTextWhenOn: 'ع',
fontWeight: FontWeight.w600, knobTextWhenOff: 'EN',
color: Colors.grey, pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300,
onTap: () {
ref.read(localeProvider.notifier).toggleLocale();
},
);
},
),
],
),
body: SafeArea(
child: Scrollbar(
thumbVisibility: true,
thickness: 6,
radius: Radius.circular(10),
interactive: true,
child:SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container
(
color: Colors.white,
padding: const EdgeInsets.all(16.0),
width: MediaQuery.of(context).size.width,
child: Text.rich(
TextSpan(
text: 'Here are some common questions about the ',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.grey,
),
children: [
TextSpan(
text:' Federal Competitiveness and Statistics Centre (FCSC),',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.black87,
),
),
TextSpan(
text: 'mobile application:',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.grey,
),
),
],
),
),
), ),
children: [ Container(
TextSpan( padding: const EdgeInsets.all(0.0),
text:' Federal Competitiveness and Statistics Centre (FCSC),', color: Colors.grey[50],
style: TextStyle( child: QuestionAnswerScrollView(),
fontSize: 19, ),
fontWeight: FontWeight.w600, ],
color: Colors.black87,
),
),
TextSpan(
text: 'mobile application:',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.grey,
),
),
],
),
), ),
), ),
Expanded(child: Scrollbar( ),
thumbVisibility: true,
thickness: 8,
radius: Radius.circular(10),
interactive: true,
child:SingleChildScrollView(
child:Container(
padding: const EdgeInsets.all(0.0),
color: Colors.grey[50],
child: QuestionAnswerScrollView(),
),
),
),),
],
),
), ),
); );
} }

View File

@ -1,8 +1,13 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/custom_text.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/custom_text.dart';
import '../../../../components/indicators/locale_provider.dart';
import '../../../../components/my_toggle.dart';
class UsingFeatures extends StatelessWidget { class UsingFeatures extends StatelessWidget {
@ -517,44 +522,74 @@ class UsingFeatures extends StatelessWidget {
]; ];
return return
BaseScaffold( title: Text('Key Features', Scaffold(
style: TextStyle( appBar: AppBar(
color: Color(0xFFAA8E83), elevation: 3,
),), shadowColor: Colors.black,
showBackButton: true, title: Text('Key Features',),
body:SafeArea(child: Column( leading: IconButton(
crossAxisAlignment: CrossAxisAlignment.start, onPressed: (){
children: [ context.go('/user-guide');
Container( },
padding: const EdgeInsets.all(16.0), color: Colors.black,
width: MediaQuery.of(context).size.width, icon: const Icon(Icons.arrow_back_ios_new),),
child: Text( actions: [
'FEATURES', Consumer(
style: TextStyle( builder: (context, ref, _) {
color: Colors.black,// Text color final locale = ref.watch(localeProvider); // Current locale
fontSize: 18, return MyToggle(
fontWeight: FontWeight.w500, isOn: locale?.languageCode == 'en',
), knobTextWhenOn: 'ع',
), knobTextWhenOff: 'EN',
pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300,
onTap: () {
ref.read(localeProvider.notifier).toggleLocale();
},
);
},
), ),
Expanded(child: Scrollbar( ],
),
body:SafeArea(
child: Scrollbar(
thumbVisibility: true, thumbVisibility: true,
thickness: 6, thickness: 6,
radius: Radius.circular(10), radius: Radius.circular(10),
interactive: true, interactive: true,
child: child: SingleChildScrollView(
Container( child: Column(
color: Colors.grey[50], // Set the background color here crossAxisAlignment: CrossAxisAlignment.start,
// child: SingleChildScrollView( children: [
child: CustomTextRich( Container(
textSpans: textSpans, color: Colors.white,
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
textAlign: TextAlign.start, width: MediaQuery.of(context).size.width,
child: Text(
'FEATURES',
style: TextStyle(
color: Colors.black,// Text color
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
),
Container(
color: Colors.grey[50], // Set the background color here
// child: SingleChildScrollView(
child: CustomTextRich(
textSpans: textSpans,
padding: const EdgeInsets.all(16.0),
textAlign: TextAlign.start,
),
),
],
), ),
), )
),), ),
], ),
), ),
); );
} }
} }

View File

@ -1,5 +1,7 @@
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/custom_text.dart'; import 'package:uae_stat/presentation/routes/drawer_routes/Drawer%20Items/user_guide/custom_text.dart';
@ -7,6 +9,9 @@ import 'package:uae_stat/presentation/routes/drawer_routes/custom_drawer_routes.
import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart'; import 'package:uae_stat/infrastructure/services/img_asset_paths/banner_asset_path.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import '../../../../components/indicators/locale_provider.dart';
import '../../../../components/my_toggle.dart';
class GettingStarted extends StatelessWidget { class GettingStarted extends StatelessWidget {
GettingStarted({super.key}); GettingStarted({super.key});
@ -66,55 +71,90 @@ class GettingStarted extends StatelessWidget {
]; ];
return BaseScaffold( title: Text('About the App', return Scaffold(
style: TextStyle( appBar: AppBar(
color:Color(0xFF265E84), elevation: 3,
fontWeight: FontWeight.w500, shadowColor: Colors.black,
title: Text('About the App',),
leading: IconButton(
onPressed: (){
context.go('/user-guide');
},
color: Colors.black,
icon: const Icon(Icons.arrow_back_ios_new),),
actions: [
Consumer(
builder: (context, ref, _) {
final locale = ref.watch(localeProvider); // Current locale
return MyToggle(
isOn: locale?.languageCode == 'en',
knobTextWhenOn: 'ع',
knobTextWhenOff: 'EN',
pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300,
onTap: () {
ref.read(localeProvider.notifier).toggleLocale();
},
);
// IconButton(
// icon: Icon(locale?.languageCode == 'en'
// ? Icons.toggle_off_outlined
// : Icons.toggle_on_outlined,),
// onPressed: () {
// ref.read(localeProvider.notifier).toggleLocale();
// },
// );
},
),
],
), ),
), // title: Text('About the App',),
showBackButton: true, // showBackButton: true,
body:SafeArea(child: Column( body:SafeArea(
crossAxisAlignment: CrossAxisAlignment.start, child: Scrollbar(
children: [ thumbVisibility: true,
Container( thickness: 6,
padding: const EdgeInsets.all(16.0), radius: Radius.circular(10),
width: MediaQuery.of(context).size.width, interactive: true,
child: Text.rich( child:SingleChildScrollView(
TextSpan( child: Column(
text: 'The', crossAxisAlignment: CrossAxisAlignment.start,
style: TextStyle( children: [
fontSize: 19, Container(
fontWeight: FontWeight.w600, color: Colors.white,
color: Colors.grey, padding: const EdgeInsets.all(16.0),
), width: MediaQuery.of(context).size.width,
children: [ child: Text.rich(
TextSpan( TextSpan(
text:' Federal Competitiveness and Statistics Centre (FCSC),', text: 'The',
style: TextStyle( style: TextStyle(
fontSize: 19, fontSize: 19,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.black87, color: Colors.grey,
), ),
), children: [
TextSpan( TextSpan(
text:' in your App is designed to provide registered and approved users with access to accurate and comprehensive statistics about the UAE. The app serves as a centralized platform for exploring key datasets, trends, and insights across various sectors.', text:' Federal Competitiveness and Statistics Centre (FCSC),',
style: TextStyle( style: TextStyle(
fontSize: 19, fontSize: 19,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Colors.grey, color: Colors.black87,
),
),
TextSpan(
text:' in your App is designed to provide registered and approved users with access to accurate and comprehensive statistics about the UAE. The app serves as a centralized platform for exploring key datasets, trends, and insights across various sectors.',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.grey,
),
),
],
), ),
), ),
], ),
),
), Column(
),
Expanded(child: Scrollbar(
thumbVisibility: true,
thickness: 6,
radius: Radius.circular(10),
interactive: true,
child: SingleChildScrollView(
child: Column(
children: [ children: [
Container( Container(
color: Colors.grey[50], // Set the background color here color: Colors.grey[50], // Set the background color here
@ -169,11 +209,11 @@ class GettingStarted extends StatelessWidget {
), ),
], ],
), ),
), ],
),
),), ),
], ),
), ), ),
); );
} }
} }

View File

@ -15,15 +15,16 @@ class _UserGuideState extends State<UserGuide> {
List<Widget> createRows(List<List<Map<String, dynamic>>> items) { List<Widget> createRows(List<List<Map<String, dynamic>>> items) {
return items.map((pair) { return items.map((pair) {
return Padding( return Padding(
padding: const EdgeInsets.only(bottom: 20), // Add space between rows padding: const EdgeInsets.only(bottom: 20),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: pair.length == 1
? MainAxisAlignment.center
: MainAxisAlignment.spaceEvenly,
children: pair.map((item) { children: pair.map((item) {
return userGuides( return userGuides(
color: item['color'] as Color, color: item['color'] as Color,
text: item['text'] as String, text: item['text'] as String,
dynamicIcon: item['icon'], dynamicIcon: item['icon'],
no: item['index'] as String,
routePath:item['routePath']as String, routePath:item['routePath']as String,
); );
}).toList(), }).toList(),
@ -36,17 +37,16 @@ class _UserGuideState extends State<UserGuide> {
title: Text('User Guide'), title: Text('User Guide'),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Container( child: Container(
color: Colors.grey[300], // Grey background for content
padding: const EdgeInsets.all(16.0), // Padding around content padding: const EdgeInsets.all(16.0), // Padding around content
child: Column( child: Column(
children: createRows([ children: createRows([
// Each pair defines a row of (Color, Text) pairs // Each pair defines a row of (Color, Text) pairs
[ [
{'routePath':'gettingStarted','color': Color(0xFF90B0D5), 'text': 'Getting Started', 'icon': 'START', 'index': '1'}, {'routePath':'gettingStarted','color': Color(0xFF90B0D5), 'text': 'Getting Started', 'icon': 'START',},
{'routePath':'features','color': Color(0xFFAA8E83), 'text': 'Using Features', 'icon': Icons.stars_outlined, 'index': '2'}, {'routePath':'features','color': Color(0xFFAA8E83), 'text': 'Using Features', 'icon': Icons.stars_outlined,},
], ],
[ [
{'routePath':'faq','color': Color(0xFF7296BE), 'text': 'FAQs', 'icon': Icons.question_answer_sharp, 'index': '3'}, {'routePath':'faq','color': Color(0xFF7296BE), 'text': 'FAQs', 'icon': Icons.question_answer_sharp,},
// {'routePath':'features','color': Color(0xFF7DAFBC), 'text': 'Advanced Settings', 'icon': Icons.settings_outlined, 'index': '6'}, // {'routePath':'features','color': Color(0xFF7DAFBC), 'text': 'Advanced Settings', 'icon': Icons.settings_outlined, 'index': '6'},
], ],
]), ]),
@ -58,21 +58,20 @@ class _UserGuideState extends State<UserGuide> {
class userGuides extends StatelessWidget { class userGuides extends StatelessWidget {
const userGuides({super.key, required this.routePath ,required this.color, required this.text,required this.dynamicIcon,required this.no}); const userGuides({super.key, required this.routePath ,required this.color, required this.text,required this.dynamicIcon});
final Color color; final Color color;
final String text; final String text;
final dynamic dynamicIcon; final dynamic dynamicIcon;
final String no;
final String routePath; final String routePath;
Widget buildDynamicWidget(dynamic icons) { Widget buildDynamicWidget(dynamic icons, Color iconColor) {
if (dynamicIcon is IconData) { if (dynamicIcon is IconData) {
return Icon( return Icon(
icons, icons,
size: 38, size: 38,
color: Colors.white, color: iconColor,
); );
} else if (dynamicIcon is String) { } else if (dynamicIcon is String) {
// Create a Text widget if variable is a String // Create a Text widget if variable is a String
@ -81,7 +80,7 @@ class userGuides extends StatelessWidget {
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
fontSize: 20, fontSize: 20,
color: Colors.white, color: iconColor,
), ),
); );
} else { } else {
@ -96,67 +95,52 @@ class userGuides extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double screenWidth= MediaQuery.of(context).size.width;
final double screenHeight= MediaQuery.of(context).size.height;
return InkWell( return InkWell(
onTap: (){ onTap: (){
context.push('/user-guide/$routePath'); context.push('/user-guide/$routePath');
}, },
child: Container( child: Container(
padding: EdgeInsets.symmetric(horizontal: 4), padding: EdgeInsets.symmetric(horizontal: 4),
height: 115, height: 110,
width: 148, width: screenWidth * 0.40,
margin: EdgeInsets.all(1), margin: EdgeInsets.all(1),
decoration: BoxDecoration( decoration: BoxDecoration(
color: color, color: Colors.white,
borderRadius: BorderRadius.circular(10), borderRadius: BorderRadius.circular(10),
), border: Border.all(color: color,
alignment: Alignment.center, width: 2.0)
child: Stack( ),
children: [Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
// Icon( // Icon(
// size:38, // size:38,
// icons, // Home icon // icons, // Home icon
// color: Colors.white, // color: Colors.white,
// ), // ),
buildDynamicWidget(dynamicIcon), buildDynamicWidget(dynamicIcon, color),
SizedBox(width: 10,height: 15,), // Space between icon and text SizedBox(width: 10,height: 10,), // Space between icon and text
Container( Container(
width: double.infinity, width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 4), // Padding inside the text background padding: EdgeInsets.symmetric(horizontal: 4, vertical: 5), // Padding inside the text background
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, // Background color for text color: color, // Background color for text
borderRadius: BorderRadius.circular(5), // Rounded corners for text background borderRadius: BorderRadius.circular(5),
), ),
child: Text( child: Text(
text, text,
style: TextStyle( style: TextStyle(
color: color, // Text color color: Colors.white, // Text color
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
), ),
], textAlign: TextAlign.center,
),
), ),
Positioned( ],
top: 6, // Distance from the top of the container ),
left: 6, // Distance from the left of the container
child: Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
color: Colors.transparent, // Background color for the number
child: Text(
no,
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
),
),],
)
), ),
); );
} }

View File

@ -1,99 +1,100 @@
import 'package:flutter/material.dart'; // import 'package:flutter/material.dart';
import 'package:flutter_carousel_widget/flutter_carousel_widget.dart'; // import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; // import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; // import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:video_player/video_player.dart'; // import 'package:video_player/video_player.dart';
//
import 'package:uae_stat/domain/use_cases/language.dart'; // import 'package:uae_stat/domain/use_cases/language.dart';
import 'package:uae_stat/infrastructure/services/img_asset_paths/user_guide_asset_path.dart'; // import 'package:uae_stat/infrastructure/services/img_asset_paths/user_guide_asset_path.dart';
import 'package:uae_stat/presentation/components/space.dart'; // import 'package:uae_stat/presentation/components/space.dart';
import 'package:uae_stat/presentation/components/themed_app_bar.dart'; // import 'package:uae_stat/presentation/components/themed_app_bar.dart';
//
typedef _UserGuideType = ({String videoPath, String enDesc, String arDesc}); // typedef _UserGuideType = ({String videoPath, String enDesc, String arDesc});
//
class UserGuideRoute extends HookConsumerWidget { // class UserGuideRoute extends HookConsumerWidget {
const UserGuideRoute({super.key}); // const UserGuideRoute({super.key});
//
static const List<_UserGuideType> _data = [ // static const List<_UserGuideType> _data = [
( // (
videoPath: UserGuideAssetPath.toggleLanguage, // videoPath: UserGuideAssetPath.toggleLanguage,
enDesc: // enDesc:
'You can toggle between Arabic and English by click the toggle button.', // 'You can toggle between Arabic and English by click the toggle button.',
arDesc: // arDesc:
'يمكنك التبديل بين العربية والإنجليزية عن طريق النقر على زر التبديل.', // 'يمكنك التبديل بين العربية والإنجليزية عن طريق النقر على زر التبديل.',
), // ),
( // (
videoPath: UserGuideAssetPath.searchIndicators, // videoPath: UserGuideAssetPath.searchIndicators,
enDesc: // enDesc:
'You can browse indicators by category or use the search functionality', // 'You can browse indicators by category or use the search functionality',
arDesc: 'يمكنك تصفح المؤشرات حسب الفئة أو استخدام وظيفة البحث', // arDesc: 'يمكنك تصفح المؤشرات حسب الفئة أو استخدام وظيفة البحث',
), // ),
( // (
videoPath: UserGuideAssetPath.bookmarkIndicators, // videoPath: UserGuideAssetPath.bookmarkIndicators,
enDesc: // enDesc:
'You can bookmark indicators. You will be notified each time a bookmarked indicator is updated by email or mobile notifications.', // 'You can bookmark indicators. You will be notified each time a bookmarked indicator is updated by email or mobile notifications.',
arDesc: // arDesc:
'يمكنك وضع إشارة مرجعية على المؤشرات. سيتم إعلامك في كل مرة يتم فيها تحديث المؤشر المرجعي عن طريق البريد الإلكتروني أو إشعارات الهاتف المحمول.', // 'يمكنك وضع إشارة مرجعية على المؤشرات. سيتم إعلامك في كل مرة يتم فيها تحديث المؤشر المرجعي عن طريق البريد الإلكتروني أو إشعارات الهاتف المحمول.',
), // ),
]; // ];
//
@override // @override
Widget build(BuildContext context, WidgetRef ref) { // Widget build(BuildContext context, WidgetRef ref) {
final e = useState<_UserGuideType>(_data.first); // final e = useState<_UserGuideType>(_data.first);
final carouselOptions = CarouselOptions( // final carouselOptions
autoPlay: true, // // final carouselOptions = CarouselOptions(
clipBehavior: Clip.none, // // autoPlay: true,
onPageChanged: (index, reason) => e.value = _data[index], // // clipBehavior: Clip.none,
aspectRatio: 9 / 17, // // onPageChanged: (index, reason) => e.value = _data[index],
disableCenter: true, // // aspectRatio: 9 / 17,
enlargeCenterPage: true, // // disableCenter: true,
floatingIndicator: true, // // enlargeCenterPage: true,
autoPlayInterval: const Duration(seconds: 10), // // floatingIndicator: true,
); // // autoPlayInterval: const Duration(seconds: 10),
final bodyContent = Column( // // );
children: [ // final bodyContent = Column(
Expanded( // children: [
flex: 3, // Expanded(
child: FlutterCarousel( // flex: 3,
options: carouselOptions, // child: FlutterCarousel(
items: _data // options: carouselOptions,
.map( // items: _data
(e) => VideoPlayer( // .map(
VideoPlayerController.asset( // (e) => VideoPlayer(
e.videoPath, // VideoPlayerController.asset(
)..initialize(), // e.videoPath,
), // )..initialize(),
) // ),
.toList(), // )
), // .toList(),
), // ),
Expanded( // ),
child: SingleChildScrollView( // Expanded(
padding: const EdgeInsets.fromLTRB(36, 24, 36, 36), // child: SingleChildScrollView(
child: Text( // padding: const EdgeInsets.fromLTRB(36, 24, 36, 36),
context.translate(e.value.enDesc, e.value.arDesc), // child: Text(
), // context.translate(e.value.enDesc, e.value.arDesc),
), // ),
), // ),
98.verticalSpace, // ),
], // 98.verticalSpace,
); // ],
final body = Column( // );
children: [ // final body = Column(
ThemedAppBar( // children: [
titleText: context.translate( // ThemedAppBar(
'User Guide', // titleText: context.translate(
'دليل المستخدم', // 'User Guide',
), // 'دليل المستخدم',
), // ),
Expanded( // ),
child: bodyContent, // Expanded(
), // child: bodyContent,
], // ),
); // ],
return ColoredBox( // );
color: Colors.white, // return ColoredBox(
child: SafeArea(child: body), // color: Colors.white,
); // child: SafeArea(child: body),
} // );
} // }
// }

View File

@ -8,6 +8,7 @@ import Foundation
import connectivity_plus import connectivity_plus
import file_selector_macos import file_selector_macos
import flutter_secure_storage_macos import flutter_secure_storage_macos
import package_info_plus
import path_provider_foundation import path_provider_foundation
import share_plus import share_plus
import shared_preferences_foundation import shared_preferences_foundation
@ -16,13 +17,14 @@ import video_player_avfoundation
import webview_flutter_wkwebview import webview_flutter_wkwebview
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin"))
FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin")) WebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "WebViewFlutterPlugin"))
} }

View File

@ -194,18 +194,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: connectivity_plus name: connectivity_plus
sha256: "224a77051d52a11fbad53dd57827594d3bd24f945af28bd70bab376d68d437f0" sha256: "8a68739d3ee113e51ad35583fdf9ab82c55d09d693d3c39da1aebab87c938412"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.2" version: "6.1.2"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_platform_interface name: connectivity_plus_platform_interface
sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.4" version: "2.0.1"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -258,18 +258,18 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: custom_lint name: custom_lint
sha256: "6d509673c4dd0baa90e60dc8366bc2acc6690f16a7d44bfae31294d82c5d2a62" sha256: "7e6a73e1722ad90b48f77918994c17a5b177b0869804ea3c4ce9c9199de019f6"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.1" version: "0.7.2"
custom_lint_builder: custom_lint_builder:
dependency: transitive dependency: transitive
description: description:
name: custom_lint_builder name: custom_lint_builder
sha256: "8cc525c7b160eb47bb1ded8b2633c0f8b907930eb986ac577aded87cdd2835fe" sha256: e26941131416b0bc81219dc8ddc68b34c6319e20abc001dd4855412749309d3b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.1" version: "0.7.2"
custom_lint_core: custom_lint_core:
dependency: transitive dependency: transitive
description: description:
@ -298,10 +298,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: dbus name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.10" version: "0.7.11"
equatable: equatable:
dependency: transitive dependency: transitive
description: description:
@ -409,10 +409,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: fl_chart name: fl_chart
sha256: "74959b99b92b9eebeed1a4049426fd67c4abc3c5a0f4d12e2877097d6a11ae08" sha256: "5276944c6ffc975ae796569a826c38a62d2abcf264e26b88fa6f482e107f4237"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.69.2" version: "0.70.2"
flutter: flutter:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -438,10 +438,10 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: flutter_launcher_icons name: flutter_launcher_icons
sha256: "31cd0885738e87c72d6f055564d37fabcdacee743b396b78c7636c169cac64f5" sha256: bfa04787c85d80ecb3f8777bde5fc10c3de809240c48fa061a2c2bf15ea5211c
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.14.2" version: "0.14.3"
flutter_lints: flutter_lints:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -557,10 +557,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: fluttertoast name: fluttertoast
sha256: "24467dc20bbe49fd63e57d8e190798c4d22cbbdac30e54209d153a15273721d1" sha256: "8971efe7e59585e9149052e33718d84bca51e806f063d1467622b3dcb2878b6c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.2.10" version: "8.2.11"
freezed: freezed:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -597,18 +597,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: glob name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
go_router: go_router:
dependency: "direct main" dependency: "direct main"
description: description:
name: go_router name: go_router
sha256: "7c2d40b59890a929824f30d442e810116caf5088482629c894b9e4478c67472d" sha256: "9b736a9fa879d8ad6df7932cbdcc58237c173ab004ef90d8377923d7ad731eaa"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.6.3" version: "14.7.2"
graphs: graphs:
dependency: transitive dependency: transitive
description: description:
@ -629,10 +629,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: hotreloader name: hotreloader
sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.2.0" version: "4.3.0"
html: html:
dependency: transitive dependency: transitive
description: description:
@ -645,10 +645,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.2" version: "1.3.0"
http_multi_server: http_multi_server:
dependency: transitive dependency: transitive
description: description:
@ -717,10 +717,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_macos name: image_picker_macos
sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.1+1" version: "0.2.1+2"
image_picker_platform_interface: image_picker_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -929,6 +929,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.1" version: "2.1.1"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
sha256: c447a3c3e7be4addf129b8f9ab6a4bd5d166b78918223e223b61fddf4d07e254
url: "https://pub.dev"
source: hosted
version: "8.2.0"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "205ec83335c2ab9107bbba3f8997f9356d72ca3c715d2f038fc773d0366b4c76"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -1140,18 +1156,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a sha256: "688ee90fbfb6989c980254a56cb26ebe9bb30a3a2dff439a78894211f73de67a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.5" version: "2.5.1"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
name: shared_preferences_android name: shared_preferences_android
sha256: bf808be89fe9dc467475e982c1db6c2faf3d2acf54d526cd5ec37d86c99dbd84 sha256: "650584dcc0a39856f369782874e562efd002a9c94aec032412c9eb81419cce1f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.1" version: "2.4.4"
shared_preferences_foundation: shared_preferences_foundation:
dependency: transitive dependency: transitive
description: description:
@ -1296,18 +1312,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: syncfusion_flutter_charts name: syncfusion_flutter_charts
sha256: "117823c9e2ffcb7fb9868c73263df88751e3bb1f3d81f617cbaf63112f530e9c" sha256: "672c17a7f4cb3020c06bb6a28d2d033623007b8e2a19704ad34b412e9bdd2dab"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "28.1.39" version: "28.2.4+1"
syncfusion_flutter_core: syncfusion_flutter_core:
dependency: transitive dependency: transitive
description: description:
name: syncfusion_flutter_core name: syncfusion_flutter_core
sha256: "794870919ca73e29c6cb25392a097cdfe58da4d6f3f3d3eccc529ecf52f78752" sha256: "3c1876b0a245de23de3b17a19e3106fed57d88f4fd2c8dc9bc1976705b1c31d5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "28.1.39" version: "28.2.4"
term_glyph: term_glyph:
dependency: transitive dependency: transitive
description: description:
@ -1340,6 +1356,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.0.2"
tutorial_coach_mark:
dependency: "direct main"
description:
name: tutorial_coach_mark
sha256: df450c88d4c812bc221afd3ff948da3dc0f44c0b4fa5dbc046d6d86f2cfc9e71
url: "https://pub.dev"
source: hosted
version: "1.2.12"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -1464,18 +1488,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: video_player_platform_interface name: video_player_platform_interface
sha256: "229d7642ccd9f3dc4aba169609dd6b5f3f443bb4cc15b82f7785fcada5af9bbb" sha256: df534476c341ab2c6a835078066fc681b8265048addd853a1e3c78740316a844
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.3" version: "6.3.0"
video_player_web: video_player_web:
dependency: transitive dependency: transitive
description: description:
name: video_player_web name: video_player_web
sha256: "881b375a934d8ebf868c7fb1423b2bfaa393a0a265fa3f733079a86536064a10" sha256: "3ef40ea6d72434edbfdba4624b90fd3a80a0740d260667d91e7ecd2d79e13476"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.3" version: "2.3.4"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:
@ -1512,10 +1536,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: web_socket_channel name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.0.2"
webview_flutter: webview_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1528,10 +1552,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: d1ee28f44894cbabb1d94cc42f9980297f689ff844d067ec50ff88d86e27d63f sha256: "5568f17a9c25c0fdd0737900fa1c2d1fee2d780bc212d9aec10c2d1f48ef0f59"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.3.0" version: "4.3.1"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1544,18 +1568,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_wkwebview name: webview_flutter_wkwebview
sha256: "4adc14ea9a770cc9e2c8f1ac734536bd40e82615bd0fa6b94be10982de656cc7" sha256: "8e0593559bfecd35eb1757d6907ed6b995a41ef82607d6113df897c2805ce6be"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.17.0" version: "3.18.0"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
sha256: "154360849a56b7b67331c21f09a386562d88903f90a1099c5987afc1912e1f29" sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.10.0" version: "5.10.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:

View File

@ -21,7 +21,7 @@ dependencies:
riverpod_annotation: ^2.3.3 riverpod_annotation: ^2.3.3
cupertino_icons: ^1.0.6 cupertino_icons: ^1.0.6
fl_chart: ^0.69.2 fl_chart: ^0.70.2
marquee: ^2.2.3 marquee: ^2.2.3
url_launcher: ^6.3.1 url_launcher: ^6.3.1
@ -60,7 +60,9 @@ dependencies:
material_charts: ^0.0.23 material_charts: ^0.0.23
flutter_staggered_grid_view: ^0.7.0 flutter_staggered_grid_view: ^0.7.0
syncfusion_flutter_charts: ^28.1.39 syncfusion_flutter_charts: ^28.1.39
connectivity_plus: ^5.0.1 connectivity_plus: ^6.1.2
tutorial_coach_mark: ^1.2.12
package_info_plus: ^8.2.0
dependency_overrides: dependency_overrides:
fading_edge_scrollview: ^4.1.1 fading_edge_scrollview: ^4.1.1