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")
if (flutterVersionCode == null) {
flutterVersionCode = "1"
flutterVersionCode = "9"
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
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 {
namespace = "ae.gov.fcsc.frontend"
compileSdk = flutter.compileSdkVersion
// ndkVersion = flutter.ndkVersion
ndkVersion = "25.1.8937393"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
defaultConfig {
@ -44,11 +52,22 @@ android {
versionName = flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// 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
android.useAndroidX=true
android.enableJetifier=true
org.gradle.java.home=C:/Program Files/Eclipse Adoptium/jdk-21.0.5.11-hotspot
org.gradleorg.gradle.java.home=/usr/lib/jvm/openlogic-openjdk-17.0.13+11-linux-x64

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/demo_home2.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/custom_drawer_routes.dart';
@ -316,6 +317,7 @@ import '../presentation/Screens/demo_home.dart';
import '../presentation/Screens/profilepage.dart';
import '../presentation/routes/auth_routes/login_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/feedback.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/manage_users.dart';
final pbService = PocketBaseService();
Future<String> getInitialRoute() async {
bool isLoggedIn = await pbService.loadSession();
return isLoggedIn ? '/myhomepage' : '/login';
}
final GoRouter router = GoRouter(
routes: [
GoRoute(
path: '/',
//builder: (context, state) => LoginRoute(),
builder: (context, state) => MyHomePage(),
builder: (context, state) => RegisterScreen(),
),
// GoRoute(
// path: '/',
// redirect: (context, state) async {
// return await getInitialRoute();
// },
// ),
GoRoute(
path: '/internetcheck',
builder: (context, state) => InternetCheck(),
),
GoRoute(
path: '/login',
@ -374,6 +388,7 @@ final GoRouter router = GoRouter(
'0xFFFFFFFF'; // Default white
final mainTopic = state.uri.queryParameters['mainTopic'] ?? '';
final title = state.uri.queryParameters['title'] ?? '';
final key = state.uri.queryParameters['key'] ?? '';
print('Router dataSets: $dataSets');
print('Router bgColor: $bgColor');
@ -386,6 +401,7 @@ final GoRouter router = GoRouter(
bgColor: bgColor,
mainTopic: mainTopic,
title: title,
keyParam: key,
);
},
),
@ -411,7 +427,9 @@ final GoRouter router = GoRouter(
builder: (context, state) {
final userId = state.pathParameters['userId']!;
final email = state.pathParameters['email']!;
return CreateNewPw(userId: userId, email: email);
final key = state.uri.queryParameters['key'] ?? '';
return CreateNewPw(userId: userId, email: email, keyParam: key);
},
),
GoRoute(
@ -468,6 +486,10 @@ final GoRouter router = GoRouter(
path: '/editProfile',
builder: (context, state) => EditProfile(),
),
GoRoute(
path: '/bookmark',
builder: (context, state) => BookMark(),
),
GoRoute(
path: '/manageuser',
builder: (context, state) => ManageUserRouter(),

View File

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

View File

@ -8,4 +8,7 @@ abstract class DrawerAssetIconPath {
static const message = '$_basePath/message.png';
static const scroll = '$_basePath/scroll.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 person = '$_basePath/person.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_failed_msg": "لم يتم إرسال تعليقاتك. يرجى المحاولة مرة أخرى",
"feedback_title": "التعليقات",
"feedback_failed_msg": "لم يتم إرسال تعليقاتك. يرجى المحاولة مرة أخرى.",
"guide_title": "نموذج الملاحظات",
"register_title": "التسجيل",
"register_details": "من فضلك ادخل بياناتك",
"register_name": "اسم المستخدم",
"email_id": "الاسم الكامل",
"email_id": "معرف البريد الإلكتروني",
"enter_name": "أدخل الاسم",
"register_email": "أدخل بريدك الإلكتروني",
"register_password": "دخل كلمة المرور",
"register_Confirm_password": "أكيد كلمة المرو",
"agree": " وافق على",
"t_and": "و ",
"conditions": " الخاصة بالمركز الاتحادي للتنافسية والإحصاء",
"terms_conditions": "الشروط والأحكام ",
"privacy_policy":" وسياسة الخصوصية",
"account_confirmation": "ل لديك حساب؟ سجل الدخول ",
"login_title": "سجيل الدخول",
"enter_your_email": "أدخل بريدك الإلكتروني",
"enter_your_password": "أدخل كلمة المرور",
"register_Confirm_password": "تأكيد كلمة المرور",
"agree": "أوافق على الشروط والأحكام وسياسة الخصوصية للمركز الإتحادي للتنافسية والإحصاء",
"t_and": "",
"conditions": "",
"terms_conditions": "الشروط والأحكام",
"privacy_policy":"سياسة الخصوصية",
"account_confirmation": "هل لديك حساب؟ سجل الدخول",
"login_title": "تسجيل الدخول",
"profile_title": "نموذج الملاحظات",
"my_profile": "ملفي الشخصي",
@ -30,8 +30,8 @@
"confirm": "تأكيد",
"change_password": "تغيير كلمة المرور",
"sure_save_page": " هل أنت متأكد أنك ترغب في حفظ هذه الصفحة؟",
"not_able_to_change": " بمجرد الحفظ، لن تتمكن من تعديل اسمك أو تاريخ ",
"save": "يحفظ",
"not_able_to_change": "بمجرد الحفظ، لن تتمكن من تعديل اسمك أو تاريخ ميلادك.",
"save": "حفظ",
"name": "اسم",
@ -55,25 +55,25 @@
"password_match": "كلمات المرور غير متطابقة",
"only8charac": "يجب أن تتكون كلمة المرور من 8 أحرف على الأقل",
"manage_user": "دارة المستخدمين",
"manage_user": "إدارة المستخدمين",
"user_name": "اسم المستخدم",
"reg_date": "تاريخ التسجيل",
"status": "تمت الموافقة",
"status": "الحالة",
"denied": "مرفوض",
"pending": "يد الانتظار",
"pending": " قيد الانتظار",
"approve_msg": "هل أنت متأكد أنك ترغب في تغيير الحالة إلى \"{newStatus}\"؟",
"yes": "نعم",
"no": "لا",
"no_result_found": "لم يتم العثور على نتائج",
"search": "يبحث",
"approved": "موافقة",
"approved": "تمت الموافقة",
"create_new_password": "إنشاء كلمة مرور جديدة",
"different_password": "يجب أن تكون كلمة المرور الجديدة مختلفة عن كلمة المرور المستخدمة سابقًا.",
"different_password":"يجب أن تكون كلمة المرور الجديدة مختلفة عن كلمة المرور المستخدمة سابقًا.",
"old_password": "كلمة المرور القديمة",
"enter_new_password": "أدخل كلمة المرور الجديدة",
"confirm_new_password": "تأكيد كلمة المرور الجديدة",
"password_changed_successfully": "م تغيير كلمة المرور بنجاح",
"password_changed_successfully": "تم تغيير كلمة المرور بنجاح",
"enter_old_password": "أدخل كلمة المرور القديمة",
"your_old_password_incorrect": "كلمة المرور القديمة الخاصة بك غير صحيحة.",
"failed_to_update_password": "",
@ -82,5 +82,13 @@
"password_between_8_to_40": "يجب أن تتراوح كلمة المرور بين 8 و64 حرفًا",
"new_password_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_name": "Username",
"email_id": "Email ID",
"enter_name": "Enter your User Name",
"register_email": "Enter your email",
"register_password": "Enter your password",
"register_Confirm_password": "Confirm password",
"agree": "I agree to ",
"t_and": "and",
"conditions": " of Fcsc",
"terms_conditions": "terms & conditions",
"privacy_policy":"privacy policy",
"account_confirmation": "Already have an account? Login",
"login_title": "Login",
"enter_name": "Enter your User Name",
"enter_your_email": "Enter your email",
"enter_your_password": "Enter your password",
"register_Confirm_password": "Confirm password",
"agree": "I agree to the ",
"t_and": " and ",
"conditions": " of FCSC",
"terms_conditions": "Terms & Conditions",
"privacy_policy":"Privacy Policy",
"account_confirmation": "Already have an account?",
"login_title": "Login",
"profile_title": "Edit Profile",
"my_profile": "My Profile",
@ -83,5 +85,13 @@
"password_between_8_to_40": "Password must be between 8 and 64 characters",
"new_password_required": "New 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) {
final locale = ref.watch(localeProvider);
ref.listen<bool>(connectivityProvider, (previous, hasInternet) {
print('Previous Internet Status: $previous');
print('Current Internet Status: $hasInternet');
if (previous != null && hasInternet != previous) {
handleConnectivityChange(context, hasInternet);
}
});
// ref.listen<bool>(connectivityProvider, (previous, hasInternet) {
// print('Previous Internet Status: $previous');
// print('Current Internet Status: $hasInternet');
// if (previous != null && hasInternet != previous) {
// handleConnectivityChange(context, hasInternet);
// }
// });
return MaterialApp.router(
debugShowCheckedModeBanner: false,

View File

@ -1,12 +1,626 @@
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/routes/drawer_routes/custom_drawer_routes.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 StatelessWidget {
class Marriages extends ConsumerStatefulWidget {
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
Widget build(BuildContext context) {
double myheight = MediaQuery.of(context).size.height;
@ -33,7 +647,7 @@ class Marriages extends StatelessWidget {
Row(
mainAxisAlignment: MainAxisAlignment.end,
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,)),
],),
],
@ -47,6 +661,7 @@ class Marriages extends StatelessWidget {
child: Column(
children: [
Row(
key:chartKey,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Container(height: myheight/3.5,width: mywidth/2.5,

View File

@ -1,7 +1,9 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.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/drawer_routes/custom_drawer_routes.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});
@override
_UaenumberWidgetState createState() => _UaenumberWidgetState();
ConsumerState<uaenumberWidget> createState() => _UaenumberWidgetState();
}
class _UaenumberWidgetState extends State<uaenumberWidget> {
class _UaenumberWidgetState extends ConsumerState<uaenumberWidget> {
@override
List<dynamic> homePageData = [];
bool isLoading = true;
int? expandedIndex = 0;
@override
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/getUAENumbersData';
try {
final response = await http.get(Uri.parse(baseUrl));
final response = await http.get(Uri.parse(baseUrl + '?language=$locale'));
if (response.statusCode == 200) {
setState(() {
homePageData = json.decode(response.body);
@ -63,6 +68,10 @@ class _UaenumberWidgetState extends State<uaenumberWidget> {
}
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 mywidth = MediaQuery.of(context).size.width;
@ -74,12 +83,13 @@ class _UaenumberWidgetState extends State<uaenumberWidget> {
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(30.0),
border: Border.all(width: 1)),
border: Border.all(width: 1,color: Color(0xFFAA8E83)),),
child: TextField(
decoration: InputDecoration(
hintText: "Search",
prefixIcon: Icon(Icons.search),
border: InputBorder.none,
prefixIcon: Icon(Icons.search,
color: Color(0xFFAA8E83),),
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(vertical: 15.0, horizontal: 20.0),
),
@ -99,7 +109,12 @@ class _UaenumberWidgetState extends State<uaenumberWidget> {
return CustomExpandableTile(
index: index,
isExpanded: isFirstTile,
isExpanded: expandedIndex == index, // Compare with expandedIndex
onTap: (index) {
setState(() {
expandedIndex = (expandedIndex == index) ? null : index;
});
},
title: mainTopic['main_topic'],
titleBackgroundColor: backgroundColor,
children: _buildSubTopics(mainTopic['sub_topics'] ?? [], mywidth,
@ -280,6 +295,8 @@ class CustomExpandableTile extends StatefulWidget {
final List<Widget> children;
final int index;
final bool isExpanded;
final ValueChanged<int> onTap;
const CustomExpandableTile({
required this.title,
@ -287,6 +304,8 @@ class CustomExpandableTile extends StatefulWidget {
required this.children,
required this.index,
required this.isExpanded,
required this.onTap,
});
@override
@ -294,14 +313,12 @@ class CustomExpandableTile extends StatefulWidget {
}
class _CustomExpandableTileState extends State<CustomExpandableTile> {
// bool isExpanded = false;
late bool isExpanded;
bool isExpanded = false;
// late bool isExpanded;
@override
void initState() {
super.initState();
isExpanded =
widget.isExpanded; // Initialize isExpanded based on widget's property
super.initState(); // Initialize isExpanded based on widget's property
}
@override
@ -312,11 +329,8 @@ class _CustomExpandableTileState extends State<CustomExpandableTile> {
child: Column(
children: [
GestureDetector(
onTap: () {
setState(() {
isExpanded = !isExpanded;
});
},
onTap: () => widget.onTap(widget.index),
child: Container(
decoration: BoxDecoration(
color: widget.titleBackgroundColor,
@ -335,7 +349,7 @@ class _CustomExpandableTileState extends State<CustomExpandableTile> {
),
),
Icon(
isExpanded
widget.isExpanded
? Icons.keyboard_arrow_up
: Icons.keyboard_arrow_down,
color: Colors.white,
@ -350,8 +364,8 @@ class _CustomExpandableTileState extends State<CustomExpandableTile> {
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
width: double.infinity,
height: isExpanded ? myheight * 0.52 : 0,
child: isExpanded
height: widget.isExpanded ? myheight * 0.52 : 0,
child: widget.isExpanded
? SingleChildScrollView(
child: Container(
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 {
final String userId;
final String email;
const CreateNewPw({Key? key, required this.userId, required this.email});
final String? keyParam;
const CreateNewPw({Key? key, required this.userId, required this.email, required this.keyParam});
@override
State<CreateNewPw> createState() => _CreateNewPwState();
@ -205,7 +207,38 @@ class _CreateNewPwState extends State<CreateNewPw> {
padding: const EdgeInsets.all(24.0),
child: Column(
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),
Text(
// "Create New Password",
AppLocalizations.of(context)!.create_new_password,
@ -327,8 +360,8 @@ class _CreateNewPwState extends State<CreateNewPw> {
SizedBox(height: screenHeight / 5),
Center(
child: Container(
height: screenHeight / 8,
width: screenWidth / 2,
height: screenHeight / 12,
width: screenWidth / 2.5,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage("assets/splash_screen/logo.png"),

File diff suppressed because it is too large Load Diff

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -13,13 +13,13 @@ class ChartData {
class ApiService {
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> nonChartData = [];
List<dynamic> originalChartsData = [];
List<dynamic> originalCardData = [];
final url = Uri.parse('$baseUrl?dataset=$dataSets');
final url = Uri.parse('$baseUrl?dataset=$dataSets&language=$locale');
try {
final response = await http.get(url);
@ -43,7 +43,8 @@ class ApiService {
return {
'isChartData': isChartData,
'nonChartData': nonChartData,
'filterData': jsonData['filter_data']
'filterData': jsonData['new_filter_data']
// 'filterData': jsonData['filter_data']
// 'originalChartsData': originalChartsData,
// '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:pocketbase/pocketbase.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 '../components/my_bottom_nav_bar.dart';
@ -74,6 +75,8 @@ class _ProfileScreenState extends State<ProfileScreen> {
// Regular expression to validate Full Name (no special characters)
final RegExp _nameRegExp = RegExp(r"^[a-zA-Z\s]+$");
late Future<RecordModel> userDetails;
bool _isHoveringDate = false;
bool _isHoveringDropdown = false;
@override
void initState() {
@ -334,418 +337,450 @@ class _ProfileScreenState extends State<ProfileScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios_new, color: Colors.black),
onPressed: () {
context.go('/');
},
),
title: Text(
'My Profile',
style: TextStyle(color: Colors.orange),
),
actions: [
TextButton(
return PopScope(
onPopInvokedWithResult: (didPop, result) {
if (didPop) return;
context.go('/');
},
child: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back_ios_new, color: Colors.black),
onPressed: () {
context.go('/');
},
child: Text(
'Logout',
style: TextStyle(color: Colors.orange),
),
title: Text(
'My Profile',
style: TextStyle(color: Color(0xFF985400)),
),
actions: [
TextButton(
onPressed: () {
context.go('/');
},
child: Text(
'Logout',
style: TextStyle(color: Colors.orange),
),
),
),
IconButton(
icon: Icon(Icons.logout, color: Colors.black),
onPressed: () {},
),
],
),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
// CircleAvatar(
// radius: 50,
// backgroundImage: _profileImage != null
// ? FileImage(_profileImage!)
// : AssetImage("assets/edit_profile/profile.png")
// as ImageProvider,
// child: Align(
// alignment: Alignment.bottomRight,
// child: GestureDetector(
// onTap: _pickImage, // Call `_pickImage` on tap
// child: CircleAvatar(
// radius: 15,
// backgroundColor: Colors.white,
// child: Icon(
// Icons.camera_alt,
// size: 15,
// color: Colors.grey,
// ),
// ),
// ),
// ),
// ),
IconButton(
icon: Icon(Icons.logout, color: Colors.black),
onPressed: () {},
),
],
),
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
top: 20.0, bottom: 20.0, left: 25, right: 25),
child: Column(
children: [
// CircleAvatar(
// radius: 50,
// backgroundImage: _profileImage != null
// ? FileImage(_profileImage!)
// : AssetImage("assets/edit_profile/profile.png")
// as ImageProvider,
// child: Align(
// alignment: Alignment.bottomRight,
// child: GestureDetector(
// onTap: _pickImage, // Call `_pickImage` on tap
// child: CircleAvatar(
// radius: 15,
// backgroundColor: Colors.white,
// child: Icon(
// Icons.camera_alt,
// size: 15,
// color: Colors.grey,
// ),
// ),
// ),
// ),
// ),
Stack(
alignment: Alignment.center,
children: [
// CircleAvatar with the profile image
CircleAvatar(
radius: 50,
backgroundImage: _profileImage != null
? FileImage(_profileImage!)
: AssetImage("assets/edit_profile/profile.png")
as ImageProvider,
child: Align(
alignment: Alignment.bottomRight,
child: GestureDetector(
onTap: _pickImage, // Call `_pickImage` on tap
child: CircleAvatar(
radius: 15,
backgroundColor: Colors.white,
child: Icon(
Icons.camera_alt,
size: 15,
color: Colors.grey,
Stack(
alignment: Alignment.center,
children: [
// CircleAvatar with the profile image
CircleAvatar(
radius: 50,
backgroundImage: _profileImage != null
? FileImage(_profileImage!)
: AssetImage("assets/edit_profile/profile.png")
as ImageProvider,
child: Align(
alignment: Alignment.bottomRight,
child: GestureDetector(
onTap: _pickImage, // Call `_pickImage` on tap
child: CircleAvatar(
radius: 15,
backgroundColor: Colors.white,
child: Icon(
Icons.camera_alt,
size: 15,
color: Colors.grey,
),
),
),
),
),
),
// Conditional loader that shows when _isLoading is true
if (_isLoading)
Positioned(
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(Colors.grey),
// Conditional loader that shows when _isLoading is true
if (_isLoading)
Positioned(
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor:
AlwaysStoppedAnimation<Color>(Colors.grey),
),
),
),
],
),
],
),
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)
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 10.0),
child: Text(
'Please agree to terms and conditions',
style: TextStyle(
color: Colors.red[700],
fontSize: 12,
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),
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),
Center(
child: GestureDetector(
onTap: () {
final userId = widget.userId;
final email = _emailController.text;
if (showError)
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Padding(
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(
"Change Password",
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline),
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF92722A),
minimumSize: Size(double.infinity, 50),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
),
),
SizedBox(height: 20),
ElevatedButton.icon(
onPressed: () {
if ((_formKey.currentState?.validate() ?? false) &&
(isChecked)) {
_formKey.currentState?.save();
showConfirmationDialog(context);
} else {
setState(() {
showError =
!isChecked; // Show error if the checkbox is not checked
});
}
// 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),
child: Row(
mainAxisAlignment:
MainAxisAlignment.center, // Center the content
children: [
Text(
'Save',
style: TextStyle(color: Colors.white),
),
SizedBox(width: 8), // Add space between text and icon
Image.asset(
MiscIconAssetPath.save,
color: Colors.white,
width: 20,
height: 20,
),
],
),
),
),
],
)
],
),
),
),
),
),
//bottomNavigationBar: MyBottomNavBar(),
);
}
}

View File

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

View File

@ -1,5 +1,6 @@
import 'package:external_repos/external_repos.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.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/space.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:http/http.dart' as http;
import '../../Screens/auth_verification/registration.dart';
import 'package:pocketbase/pocketbase.dart';
@ -26,7 +28,10 @@ import '../../components/my_toggle.dart';
class LoginRoute extends HookConsumerWidget {
final pb = PocketBase('https://pb.venbait.in');
final _pbService = PocketBaseService();
// 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});
dynamic userData;
String? role;
@ -93,52 +98,91 @@ class LoginRoute extends HookConsumerWidget {
final forgotPwFormKey = GlobalKey<FormState>();
final email = await showDialog<String>(
context: context,
builder: (dialogCtx) => AlertDialog(
title: Text(
context.translate(
'Reset Password',
'إعادة تعيين كلمة المرور',
builder: (dialogCtx) => SizedBox(
// width: 850, // Increase dialog width
child: AlertDialog(
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;
@ -174,7 +218,7 @@ class LoginRoute extends HookConsumerWidget {
),
fontWeight: FontWeight.bold,
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;
final userId = session.id;
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);
}
try {
@ -340,7 +400,10 @@ class LoginRoute extends HookConsumerWidget {
),
),
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',
'بريد إلكتروني',
),
validator: FieldValidator.email(),
validator: (value) {
if (value == null || value.isEmpty) {
return context.translate('Required', 'مطلوب');
}
return FieldValidator.email()(value);
},
imgPath: MiscIconAssetPath.person,
controller: emailCtl,
),
15.verticalSpace,
ThemedFormField(
validator: (text) {
if (text!.length < 8) {
if (text == null || text.isEmpty) {
return context.translate('Required', 'مطلوب');
} else if (text.length < 8) {
return 'The password must be at least 8 characters';
}
return FieldValidator.password(minLength: 8)(text);
@ -375,9 +445,12 @@ class LoginRoute extends HookConsumerWidget {
isObscurable: true,
),
// 6.verticalSpace,
Align(
alignment: AlignmentDirectional.topEnd,
child: forgotPwBtn,
Padding(
padding: EdgeInsets.zero,
child: Align(
alignment: AlignmentDirectional.topEnd,
child: forgotPwBtn,
),
),
10.verticalSpace,
loginBtn,
@ -403,7 +476,7 @@ class LoginRoute extends HookConsumerWidget {
10.verticalSpace,
Text(
context.translate(
'Please login to access UAEs key official statistics',
'Please login to access \n UAEs key official statistics',
'يرجى تسجيل الدخول للوصول إلى الإحصاءات الرسمية الرئيسية لدولة الإمارات العربية المتحدة',
),
textAlign: TextAlign.center,
@ -412,9 +485,9 @@ class LoginRoute extends HookConsumerWidget {
'Roboto',
'NotoKufi',
),
fontSize: 18,
fontSize: context.translate(18.0, 14.0),
color: const Color(0xff898C81),
fontWeight: FontWeight.w700,
fontWeight: FontWeight.w600,
),
),
],
@ -429,7 +502,7 @@ class LoginRoute extends HookConsumerWidget {
'Roboto',
'NotoKufi',
),
fontSize: 18,
fontSize: 14,
fontWeight: FontWeight.bold,
),
children: [
@ -451,63 +524,71 @@ class LoginRoute extends HookConsumerWidget {
'سجل الان',
),
style: TextStyle(
color: MyTheme.topicColor(IndicatorTopic.economy).shade600,
color: Color(0xFF985400),
),
),
],
),
),
);
final continueAsGuestBtn = SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () =>
context.go('/myhomepage'),
//context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
style: ButtonStyle(
shape: WidgetStatePropertyAll(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
padding: const WidgetStatePropertyAll(
EdgeInsets.symmetric(vertical: 10.5),
),
textStyle: WidgetStatePropertyAll(
TextStyle(
fontFamily: context.translate(
'Roboto',
'NotoKufi',
),
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
backgroundColor: WidgetStatePropertyAll(
MyTheme.topicColor(IndicatorTopic.environment),
),
foregroundColor: const WidgetStatePropertyAll(
Colors.white,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.translate(
'Continue as Guest',
'استمر كضيف',
),
),
6.horizontalSpace,
const Icon(Icons.chevron_right_outlined),
],
),
),
);
// final continueAsGuestBtn = SizedBox(
// width: double.infinity,
// child: ElevatedButton(
// onPressed: () async {
// final prefs = await SharedPreferences.getInstance();
// prefs.clear();
// final userId = 'guest';
// if (userId.isNotEmpty) {
// await saveUserId(userId);
// }
// context.go('/myhomepage');
// //context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
// },
// //context.go('/${context.language}/${BottomNavBarItem.home.routePath}'),
// style: ButtonStyle(
// shape: WidgetStatePropertyAll(
// RoundedRectangleBorder(
// borderRadius: BorderRadius.circular(10),
// ),
// ),
// padding: const WidgetStatePropertyAll(
// EdgeInsets.symmetric(vertical: 10.5),
// ),
// textStyle: WidgetStatePropertyAll(
// TextStyle(
// fontFamily: context.translate(
// 'Roboto',
// 'NotoKufi',
// ),
// fontSize: 16,
// fontWeight: FontWeight.w600,
// ),
// ),
// backgroundColor: WidgetStatePropertyAll(
// MyTheme.topicColor(IndicatorTopic.environment),
// ),
// foregroundColor: const WidgetStatePropertyAll(
// Colors.white,
// ),
// ),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Text(
// context.translate(
// 'Continue as Guest',
// 'استمر كضيف',
// ),
// ),
// 6.horizontalSpace,
// const Icon(Icons.chevron_right_outlined),
// ],
// ),
// ),
// );
final fcscBanner = Image.asset(
BannerAssetPath.fcsc,
height: 56,
height: 40,
);
final screenWidth = MediaQuery.of(context).size.width;
final listViewHorizontalPadding =
@ -520,31 +601,64 @@ class LoginRoute extends HookConsumerWidget {
36.verticalSpace,
Align(
alignment: AlignmentDirectional.topEnd,
child: MyToggle(isOn: locale?.languageCode == 'en',
child: MyToggle(
isOn: locale?.languageCode == 'en',
knobTextWhenOn: 'ع',
knobTextWhenOff: 'EN',
pathColorWhenOn: Colors.grey.shade300,
pathColorWhenOff: Colors.grey.shade300,
onTap: (){
onTap: () {
ref.read(localeProvider.notifier).toggleLocale();
},),
},
),
),
16.verticalSpace,
helloAndPleaseLoginTexts,
42.verticalSpace,
form,
20.verticalSpace,
15.verticalSpace,
dontHaveAnAccountRegisterBtn,
36.verticalSpace,
continueAsGuestBtn,
72.verticalSpace,
20.verticalSpace,
// continueAsGuestBtn,
95.verticalSpace,
fcscBanner,
],
);
final bgScaffold = Scaffold(
backgroundColor: Colors.white,
body: SafeArea(child: scaffoldBody),
// final bgScaffold = Scaffold(
// backgroundColor: Colors.white,
// 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 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.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:http/http.dart' as http;
class MyHomePage extends StatefulWidget {
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
void handleInfoCardClick(BuildContext context, String data, Color color) {
// 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> {
class _MyHomePageState extends ConsumerState<MyHomePage> {
@override
Widget build(BuildContext context) {
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});
@override
EconomyStatsState createState() => EconomyStatsState();
ConsumerState createState() => EconomyStatsState();
}
class EconomyStatsState extends State<EconomyStatsWidget> {
class EconomyStatsState extends ConsumerState<EconomyStatsWidget> {
List<dynamic> data = [];
final _pb = PocketBase('https://pb.venbait.in');
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
void 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';
try {
final response = await http.get(Uri.parse(baseUrl));
final response = await http.get(Uri.parse(baseUrl + '?language=$locale'));
if (response.statusCode == 200) {
setState(() {
data = json.decode(response.body);
@ -283,6 +898,10 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
}
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 mywidth = MediaQuery.of(context).size.width;
@ -336,6 +955,9 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RoundedCornerContainer(
key: mainTopic['main_topic'] == 'ECONOMY'
? cardTopicKey
: null,
text: mainTopic['main_topic'],
backgroundColor: backgroundColor,
textStyle: const TextStyle(
@ -347,6 +969,9 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
SizedBox(
height: myheight / 4.8, // Set dynamic height
child: Container(
key: mainTopic['main_topic'] == 'ECONOMY'
? cardsKey
: null,
// color: Colors.grey[200], // Set a background color for the scrollable container
child: ConstrainedBox(
constraints: BoxConstraints(
@ -386,7 +1011,8 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
children: _buildRows(
[tileData],
borderColor,
mainTopic['color_pattern']),
mainTopic['color_pattern'],
mainTopic['main_topic']),
),
),
],
@ -452,8 +1078,8 @@ class EconomyStatsState extends State<EconomyStatsWidget> {
// return rows;
// }
List<Widget> _buildRows(
List<Map<String, dynamic>> tileData, Color borderColor, color_pattern) {
List<Widget> _buildRows(List<Map<String, dynamic>> tileData, Color borderColor,
String colorPattern, String mainTopic) {
// tileData.sort((a, b) =>
// (a['data_set_list_order'] ?? 0).compareTo(b['data_set_list_order'] ?? 0),);
@ -479,8 +1105,10 @@ List<Widget> _buildRows(
dataset: tile['data_set']!,
bordercolor: borderColor,
textcolor: borderColor,
colorPattern: colorPattern,
backgroundColor:
Colors.white, // Set background color for InfoCard
mainTopic: mainTopic,
onTap: () {},
),
);
@ -529,10 +1157,12 @@ class RoundedCornerContainer extends StatelessWidget {
class InfoCard extends StatelessWidget {
final String title;
final String mainTopic;
final String subtitle;
final String value;
final String dataset;
final Color bordercolor;
final String colorPattern;
final Color? textcolor;
final VoidCallback onTap;
final Color backgroundColor;
@ -544,9 +1174,11 @@ class InfoCard extends StatelessWidget {
required this.value,
required this.dataset,
required this.bordercolor,
required this.colorPattern,
this.textcolor,
required this.onTap,
required this.backgroundColor,
required this.mainTopic,
}) : super(key: key);
@override
@ -554,11 +1186,15 @@ class InfoCard extends StatelessWidget {
double myheight = MediaQuery.of(context).size.height;
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(
onTap: () {
// Call handleInfoCardClick and pass the title and color
handleInfoCardClick(
context, dataset, bordercolor); // Pass 'title' to the function
context.go(
'/chartScreen/$dataset?bgColor=$colorPattern&mainTopic=$encodedMainTopic&title=$encodedTitle&key=$encodedKey');
},
child: Container(
height: myheight / 12,
@ -589,6 +1225,7 @@ class InfoCard extends StatelessWidget {
fontSize: 11,
fontWeight: FontWeight.w400,
// color: Colors.black,
fontFamily: 'Roboto',
color: Color(0xFF000000),
),
),
@ -608,6 +1245,7 @@ class InfoCard extends StatelessWidget {
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 11,
fontFamily: 'Roboto',
fontWeight: FontWeight.w400,
color: Color(0xFF8E8E8E),
),
@ -626,8 +1264,9 @@ class InfoCard extends StatelessWidget {
Text(
value,
style: TextStyle(
fontSize: 26,
fontSize: 18,
fontWeight: FontWeight.w900,
fontFamily: 'Roboto',
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>
with WidgetsBindingObserver {
final _pb =
PocketBase('https://pb.venbait.in'); // Initialize PocketBase client
PocketBase('https://pb.venbait.in'); // Initialize PocketBase client
// final _pb =
// PocketBase('http://127.0.0.1:8090'); // Initialize PocketBase client
@ -46,32 +46,32 @@ class _FeedbackFormState extends State<FeedbackForm>
dynamic userId;
List<Map<String, dynamic>> get _emojiOptions => [
{
"icon": Icons.sentiment_very_dissatisfied,
"label": context.translate("Terrible", "رهيب"), // Translate here
"value": 1,
},
{
"icon": Icons.sentiment_dissatisfied,
"label": context.translate("Bad", "سيء"), // Translate here
"value": 2,
},
{
"icon": Icons.sentiment_neutral,
"label": context.translate("Okay", "تمام"), // Translate here
"value": 3,
},
{
"icon": Icons.sentiment_satisfied,
"label": context.translate("Good", "جيد"), // Translate here
"value": 4,
},
{
"icon": Icons.sentiment_very_satisfied,
"label": context.translate("Amazing", "مدهش"), // Translate here
"value": 5,
},
];
{
"icon": Icons.sentiment_very_dissatisfied,
"label": context.translate("Terrible", "سئ جدا"), // Translate here
"value": 1,
},
{
"icon": Icons.sentiment_dissatisfied,
"label": context.translate("Bad", "سيء"), // Translate here
"value": 2,
},
{
"icon": Icons.sentiment_neutral,
"label": context.translate("Okay", "مقبول"), // Translate here
"value": 3,
},
{
"icon": Icons.sentiment_satisfied,
"label": context.translate("Good", "جيد"), // Translate here
"value": 4,
},
{
"icon": Icons.sentiment_very_satisfied,
"label": context.translate("Amazing", "مذهل"), // Translate here
"value": 5,
},
];
static const int _characterLimit = 1200;
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),
// appBar: AppBar(
// backgroundColor: const Color(0xFFf8f9ff),
// title: Text(context.translate('Feedback Form', 'نموذج الملاحظات')),
// title: Text(AppLocalizations.of(context)!.feedback_title),
// actions: [
// Align(
// alignment: AlignmentDirectional.topEnd,
@ -235,8 +235,8 @@ class _FeedbackFormState extends State<FeedbackForm>
child: _isFeedbackSubmitted
? _buildThankYouMessage()
: _isFeedbackFailed
? _buildFailureMessage()
: _buildFeedbackForm(),
? _buildFailureMessage()
: _buildFeedbackForm(),
),
),
);
@ -249,7 +249,7 @@ class _FeedbackFormState extends State<FeedbackForm>
Text(
context.translate(
'Do you have a suggestion or had any problem? Let us know.',
'هل لديك اقتراح أو واجهت أي مشكلة؟ اسمحوا لنا أن نعرف.',
'هل لديك اقتراح أو واجهت أي مشكلة؟ أخبرنا',
),
style: const TextStyle(
fontSize: 18,
@ -309,7 +309,7 @@ class _FeedbackFormState extends State<FeedbackForm>
const SizedBox(height: 30),
Text(
context.translate('How good did we do in these aspects?',
'ما مدى جودة ما قمنا به في هذه الجوانب؟'),
'ما مدى جودة أدائنا في هذه الجوانب؟'),
style: const TextStyle(
fontSize: 18,
fontFamily: 'Roboto',
@ -321,34 +321,34 @@ class _FeedbackFormState extends State<FeedbackForm>
_buildRatingRow(
context.translate('Ease of use', 'سهولة الاستخدام'),
_easeOfUseRating,
(rating) {
(rating) {
setState(() {
_easeOfUseRating = rating;
});
},
),
_buildRatingRow(
context.translate('Quality', 'جودة'),
context.translate('Quality', 'الجودة'),
_qualityRating,
(rating) {
(rating) {
setState(() {
_qualityRating = rating;
});
},
),
_buildRatingRow(
context.translate('Design', 'تصميم'),
context.translate('Design', 'التصميم'),
_designRating,
(rating) {
(rating) {
setState(() {
_designRating = rating;
});
},
),
_buildRatingRow(
context.translate('Redundant', 'متكرر'),
context.translate('Redundant', 'مكرر'),
_redundancyRating,
(rating) {
(rating) {
setState(() {
_redundancyRating = rating;
});
@ -362,14 +362,14 @@ class _FeedbackFormState extends State<FeedbackForm>
maxLength: _characterLimit,
decoration: InputDecoration(
hintText: context.translate(
'Tell us how we can improve', 'أخبرنا كيف يمكننا تحسين'),
'Tell us how we can improve', 'أخبرنا كيف يمكننا التحسين'),
border: OutlineInputBorder(
borderSide: BorderSide(
color: _hasError ? Colors.red : Color(0xFF7296BE),
),
),
errorText:
_hasError ? _errorMessage : null, // Show error below field
_hasError ? _errorMessage : null, // Show error below field
),
onChanged: (text) {
// Trigger re-validation and character limit on each change
@ -412,7 +412,7 @@ class _FeedbackFormState extends State<FeedbackForm>
Text(
context.translate(
'Submit',
'يُقدِّم',
'إرسال',
),
),
6.horizontalSpace,
@ -514,10 +514,10 @@ class _FeedbackFormState extends State<FeedbackForm>
}
Widget _buildRatingRow(
String label,
double rating,
Function(double) onRatingUpdate,
) {
String label,
double rating,
Function(double) onRatingUpdate,
) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -573,7 +573,7 @@ class _FeedbackFormState extends State<FeedbackForm>
"design": _designRating,
"redundancy": _redundancyRating,
"emoji_rating": _emojiOptions[_selectedEmojiIndex!]
["label"], // Emoji rating
["label"], // Emoji rating
"feedback": _feedbackController.text,
};
@ -783,7 +783,7 @@ class LangToggle extends ConsumerWidget {
ref.read(preferencesUseCaseProvider.notifier).updatePreferences(
(prefs) => prefs.copyWith(language: languageAfter),
);
);
await onLoadFeedback(); // Reload feedback text after toggling language
},

View File

@ -1,7 +1,12 @@
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 '../../../../components/indicators/locale_provider.dart';
import '../../../../components/my_toggle.dart';
class FAQPage extends StatefulWidget {
const FAQPage({super.key});
@ -12,63 +17,90 @@ class FAQPage extends StatefulWidget {
class _FAQPageState extends State<FAQPage> {
@override
Widget build(BuildContext context) {
return BaseScaffold(
title: Text('FAQs',
style: TextStyle(
color:Color(0xFF7296BE),
fontWeight: FontWeight.w500,
),),
showBackButton: true,
body: SafeArea(child:Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
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,
return Scaffold(
appBar: AppBar(
elevation: 3,
shadowColor: Colors.black,
title: Text('Key Features',),
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();
},
);
},
),
],
),
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: [
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,
),
),
],
),
Container(
padding: const EdgeInsets.all(0.0),
color: Colors.grey[50],
child: QuestionAnswerScrollView(),
),
],
),
),
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_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/Drawer%20Items/user_guide/custom_text.dart';
import '../../../../components/indicators/locale_provider.dart';
import '../../../../components/my_toggle.dart';
class UsingFeatures extends StatelessWidget {
@ -517,44 +522,74 @@ class UsingFeatures extends StatelessWidget {
];
return
BaseScaffold( title: Text('Key Features',
style: TextStyle(
color: Color(0xFFAA8E83),
),),
showBackButton: true,
body:SafeArea(child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(16.0),
width: MediaQuery.of(context).size.width,
child: Text(
'FEATURES',
style: TextStyle(
color: Colors.black,// Text color
fontSize: 18,
fontWeight: FontWeight.w500,
),
),
Scaffold(
appBar: AppBar(
elevation: 3,
shadowColor: Colors.black,
title: Text('Key Features',),
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();
},
);
},
),
Expanded(child: Scrollbar(
],
),
body:SafeArea(
child: Scrollbar(
thumbVisibility: true,
thickness: 6,
radius: Radius.circular(10),
interactive: true,
child:
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,
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(
'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/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';
@ -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:url_launcher/url_launcher.dart';
import '../../../../components/indicators/locale_provider.dart';
import '../../../../components/my_toggle.dart';
class GettingStarted extends StatelessWidget {
GettingStarted({super.key});
@ -66,55 +71,90 @@ class GettingStarted extends StatelessWidget {
];
return BaseScaffold( title: Text('About the App',
style: TextStyle(
color:Color(0xFF265E84),
fontWeight: FontWeight.w500,
return Scaffold(
appBar: AppBar(
elevation: 3,
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();
// },
// );
},
),
],
),
),
showBackButton: true,
body:SafeArea(child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: const EdgeInsets.all(16.0),
width: MediaQuery.of(context).size.width,
child: Text.rich(
TextSpan(
text: 'The',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.grey,
),
children: [
TextSpan(
text:' Federal Competitiveness and Statistics Centre (FCSC),',
// title: Text('About the App',),
// showBackButton: true,
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: 'The',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
color: Colors.black87,
color: Colors.grey,
),
),
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,
children: [
TextSpan(
text:' Federal Competitiveness and Statistics Centre (FCSC),',
style: TextStyle(
fontSize: 19,
fontWeight: FontWeight.w600,
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,
),
),
],
),
),
],
),
),
),
Expanded(child: Scrollbar(
thumbVisibility: true,
thickness: 6,
radius: Radius.circular(10),
interactive: true,
child: SingleChildScrollView(
child: Column(
),
Column(
children: [
Container(
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) {
return items.map((pair) {
return Padding(
padding: const EdgeInsets.only(bottom: 20), // Add space between rows
padding: const EdgeInsets.only(bottom: 20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisAlignment: pair.length == 1
? MainAxisAlignment.center
: MainAxisAlignment.spaceEvenly,
children: pair.map((item) {
return userGuides(
color: item['color'] as Color,
text: item['text'] as String,
dynamicIcon: item['icon'],
no: item['index'] as String,
routePath:item['routePath']as String,
);
}).toList(),
@ -36,17 +37,16 @@ class _UserGuideState extends State<UserGuide> {
title: Text('User Guide'),
body: SingleChildScrollView(
child: Container(
color: Colors.grey[300], // Grey background for content
padding: const EdgeInsets.all(16.0), // Padding around content
child: Column(
children: createRows([
// Each pair defines a row of (Color, Text) pairs
[
{'routePath':'gettingStarted','color': Color(0xFF90B0D5), 'text': 'Getting Started', 'icon': 'START', 'index': '1'},
{'routePath':'features','color': Color(0xFFAA8E83), 'text': 'Using Features', 'icon': Icons.stars_outlined, 'index': '2'},
{'routePath':'gettingStarted','color': Color(0xFF90B0D5), 'text': 'Getting Started', 'icon': 'START',},
{'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'},
],
]),
@ -58,21 +58,20 @@ class _UserGuideState extends State<UserGuide> {
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 String text;
final dynamic dynamicIcon;
final String no;
final String routePath;
Widget buildDynamicWidget(dynamic icons) {
Widget buildDynamicWidget(dynamic icons, Color iconColor) {
if (dynamicIcon is IconData) {
return Icon(
icons,
size: 38,
color: Colors.white,
color: iconColor,
);
} else if (dynamicIcon is String) {
// Create a Text widget if variable is a String
@ -81,7 +80,7 @@ class userGuides extends StatelessWidget {
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 20,
color: Colors.white,
color: iconColor,
),
);
} else {
@ -96,67 +95,52 @@ class userGuides extends StatelessWidget {
@override
Widget build(BuildContext context) {
final double screenWidth= MediaQuery.of(context).size.width;
final double screenHeight= MediaQuery.of(context).size.height;
return InkWell(
onTap: (){
context.push('/user-guide/$routePath');
},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 4),
height: 115,
width: 148,
margin: EdgeInsets.all(1),
decoration: BoxDecoration(
color: color,
padding: EdgeInsets.symmetric(horizontal: 4),
height: 110,
width: screenWidth * 0.40,
margin: EdgeInsets.all(1),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
),
alignment: Alignment.center,
child: Stack(
children: [Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Icon(
// size:38,
// icons, // Home icon
// color: Colors.white,
// ),
buildDynamicWidget(dynamicIcon),
border: Border.all(color: color,
width: 2.0)
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Icon(
// size:38,
// icons, // Home icon
// color: Colors.white,
// ),
buildDynamicWidget(dynamicIcon, color),
SizedBox(width: 10,height: 15,), // Space between icon and text
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 4), // Padding inside the text background
decoration: BoxDecoration(
color: Colors.white, // Background color for text
borderRadius: BorderRadius.circular(5), // Rounded corners for text background
),
child: Text(
text,
style: TextStyle(
color: color, // Text color
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
SizedBox(width: 10,height: 10,), // Space between icon and text
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(horizontal: 4, vertical: 5), // Padding inside the text background
decoration: BoxDecoration(
color: color, // Background color for text
borderRadius: BorderRadius.circular(5),
),
child: Text(
text,
style: TextStyle(
color: Colors.white, // Text color
fontWeight: FontWeight.bold,
),
],
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_carousel_widget/flutter_carousel_widget.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:video_player/video_player.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/presentation/components/space.dart';
import 'package:uae_stat/presentation/components/themed_app_bar.dart';
typedef _UserGuideType = ({String videoPath, String enDesc, String arDesc});
class UserGuideRoute extends HookConsumerWidget {
const UserGuideRoute({super.key});
static const List<_UserGuideType> _data = [
(
videoPath: UserGuideAssetPath.toggleLanguage,
enDesc:
'You can toggle between Arabic and English by click the toggle button.',
arDesc:
'يمكنك التبديل بين العربية والإنجليزية عن طريق النقر على زر التبديل.',
),
(
videoPath: UserGuideAssetPath.searchIndicators,
enDesc:
'You can browse indicators by category or use the search functionality',
arDesc: 'يمكنك تصفح المؤشرات حسب الفئة أو استخدام وظيفة البحث',
),
(
videoPath: UserGuideAssetPath.bookmarkIndicators,
enDesc:
'You can bookmark indicators. You will be notified each time a bookmarked indicator is updated by email or mobile notifications.',
arDesc:
'يمكنك وضع إشارة مرجعية على المؤشرات. سيتم إعلامك في كل مرة يتم فيها تحديث المؤشر المرجعي عن طريق البريد الإلكتروني أو إشعارات الهاتف المحمول.',
),
];
@override
Widget build(BuildContext context, WidgetRef ref) {
final e = useState<_UserGuideType>(_data.first);
final carouselOptions = CarouselOptions(
autoPlay: true,
clipBehavior: Clip.none,
onPageChanged: (index, reason) => e.value = _data[index],
aspectRatio: 9 / 17,
disableCenter: true,
enlargeCenterPage: true,
floatingIndicator: true,
autoPlayInterval: const Duration(seconds: 10),
);
final bodyContent = Column(
children: [
Expanded(
flex: 3,
child: FlutterCarousel(
options: carouselOptions,
items: _data
.map(
(e) => VideoPlayer(
VideoPlayerController.asset(
e.videoPath,
)..initialize(),
),
)
.toList(),
),
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(36, 24, 36, 36),
child: Text(
context.translate(e.value.enDesc, e.value.arDesc),
),
),
),
98.verticalSpace,
],
);
final body = Column(
children: [
ThemedAppBar(
titleText: context.translate(
'User Guide',
'دليل المستخدم',
),
),
Expanded(
child: bodyContent,
),
],
);
return ColoredBox(
color: Colors.white,
child: SafeArea(child: body),
);
}
}
// import 'package:flutter/material.dart';
// import 'package:flutter_carousel_widget/flutter_carousel_widget.dart';
// import 'package:flutter_hooks/flutter_hooks.dart';
// import 'package:hooks_riverpod/hooks_riverpod.dart';
// import 'package:video_player/video_player.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/presentation/components/space.dart';
// import 'package:uae_stat/presentation/components/themed_app_bar.dart';
//
// typedef _UserGuideType = ({String videoPath, String enDesc, String arDesc});
//
// class UserGuideRoute extends HookConsumerWidget {
// const UserGuideRoute({super.key});
//
// static const List<_UserGuideType> _data = [
// (
// videoPath: UserGuideAssetPath.toggleLanguage,
// enDesc:
// 'You can toggle between Arabic and English by click the toggle button.',
// arDesc:
// 'يمكنك التبديل بين العربية والإنجليزية عن طريق النقر على زر التبديل.',
// ),
// (
// videoPath: UserGuideAssetPath.searchIndicators,
// enDesc:
// 'You can browse indicators by category or use the search functionality',
// arDesc: 'يمكنك تصفح المؤشرات حسب الفئة أو استخدام وظيفة البحث',
// ),
// (
// videoPath: UserGuideAssetPath.bookmarkIndicators,
// enDesc:
// 'You can bookmark indicators. You will be notified each time a bookmarked indicator is updated by email or mobile notifications.',
// arDesc:
// 'يمكنك وضع إشارة مرجعية على المؤشرات. سيتم إعلامك في كل مرة يتم فيها تحديث المؤشر المرجعي عن طريق البريد الإلكتروني أو إشعارات الهاتف المحمول.',
// ),
// ];
//
// @override
// Widget build(BuildContext context, WidgetRef ref) {
// final e = useState<_UserGuideType>(_data.first);
// final carouselOptions
// // final carouselOptions = CarouselOptions(
// // autoPlay: true,
// // clipBehavior: Clip.none,
// // onPageChanged: (index, reason) => e.value = _data[index],
// // aspectRatio: 9 / 17,
// // disableCenter: true,
// // enlargeCenterPage: true,
// // floatingIndicator: true,
// // autoPlayInterval: const Duration(seconds: 10),
// // );
// final bodyContent = Column(
// children: [
// Expanded(
// flex: 3,
// child: FlutterCarousel(
// options: carouselOptions,
// items: _data
// .map(
// (e) => VideoPlayer(
// VideoPlayerController.asset(
// e.videoPath,
// )..initialize(),
// ),
// )
// .toList(),
// ),
// ),
// Expanded(
// child: SingleChildScrollView(
// padding: const EdgeInsets.fromLTRB(36, 24, 36, 36),
// child: Text(
// context.translate(e.value.enDesc, e.value.arDesc),
// ),
// ),
// ),
// 98.verticalSpace,
// ],
// );
// final body = Column(
// children: [
// ThemedAppBar(
// titleText: context.translate(
// 'User Guide',
// 'دليل المستخدم',
// ),
// ),
// Expanded(
// child: bodyContent,
// ),
// ],
// );
// return ColoredBox(
// color: Colors.white,
// child: SafeArea(child: body),
// );
// }
// }

View File

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

View File

@ -21,7 +21,7 @@ dependencies:
riverpod_annotation: ^2.3.3
cupertino_icons: ^1.0.6
fl_chart: ^0.69.2
fl_chart: ^0.70.2
marquee: ^2.2.3
url_launcher: ^6.3.1
@ -60,7 +60,9 @@ dependencies:
material_charts: ^0.0.23
flutter_staggered_grid_view: ^0.7.0
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:
fading_edge_scrollview: ^4.1.1