diff --git a/assets/app_tour/down_right.png b/assets/app_tour/down_right.png new file mode 100644 index 00000000..be10a1bb Binary files /dev/null and b/assets/app_tour/down_right.png differ diff --git a/assets/app_tour/leftDown.png b/assets/app_tour/leftDown.png new file mode 100644 index 00000000..4ba2314e Binary files /dev/null and b/assets/app_tour/leftDown.png differ diff --git a/assets/app_tour/leftUp.png b/assets/app_tour/leftUp.png new file mode 100644 index 00000000..56df6adc Binary files /dev/null and b/assets/app_tour/leftUp.png differ diff --git a/lib/l10n/intl_ar.arb b/lib/l10n/intl_ar.arb index a191b63e..1f5eb600 100644 --- a/lib/l10n/intl_ar.arb +++ b/lib/l10n/intl_ar.arb @@ -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": "يمكنك الوصول إلى الصفحات المخصصة والميزات الإضافية عن طريق تحديد الخيارات من القائمة الرئيسية." } \ No newline at end of file diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index b034d44b..dac0f961 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -13,7 +13,7 @@ "enter_your_password": "Enter your password", "register_Confirm_password": "Confirm password", "agree": "I agree to ", - "t_and": "and", + "t_and": " and ", "conditions": " of Fcsc", "terms_conditions": "terms & conditions", "privacy_policy":"privacy policy", @@ -83,5 +83,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": "Use the navigation bar to quickly access Profile, Feedback, User Guide, Notifications, and other key app features.", + "mainMenu": "Access dedicated pages and additional features by selecting options from the Main Menu." } diff --git a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/Social/marriages.dart b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/Social/marriages.dart index 0a9a7e50..c4541382 100644 --- a/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/Social/marriages.dart +++ b/lib/presentation/Screens/Bottom Navigation Pages/UAE_Numbers/Social/marriages.dart @@ -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 createState() => _MarriagesState(); +} + +class _MarriagesState extends ConsumerState { + late List marriageTargets; + late List 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, diff --git a/lib/presentation/Screens/app_tour/Target_content.dart b/lib/presentation/Screens/app_tour/Target_content.dart new file mode 100644 index 00000000..51e52b06 --- /dev/null +++ b/lib/presentation/Screens/app_tour/Target_content.dart @@ -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((ref) => true); +final chartsTourProvider = StateProvider((ref) => true); +final previousChartsTourProvider = StateProvider((ref) => true); +final homeTourProvider = StateProvider((ref) => false); +final previousHomeTourProvider = StateProvider((ref) => true); +final scaffoldTourProvider = StateProvider((ref) => true); +final previousScaffoldTourProvider = StateProvider((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,), + ], + ), + ], + ); + }, + ); +} \ No newline at end of file diff --git a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart index 260f7692..779033df 100644 --- a/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart +++ b/lib/presentation/routes/bottom_bar_routes/tab_routes/home_route.dart @@ -163,8 +163,12 @@ 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: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; @@ -215,26 +219,571 @@ class _MyHomePageState extends State { } } -class EconomyStatsWidget extends StatefulWidget { +class EconomyStatsWidget extends ConsumerStatefulWidget { const EconomyStatsWidget({super.key}); @override EconomyStatsState createState() => EconomyStatsState(); } -class EconomyStatsState extends State { +class EconomyStatsState extends ConsumerState { List data = []; bool isLoading = true; + final GlobalKey cardTopicKey = GlobalKey(); + final GlobalKey cardsKey = GlobalKey(); + late TutorialCoachMark tutorialCoachMark; + late List homeTargets; + late List previousHomeTargets; + Offset _imageCardPosition =Offset.zero; + Offset _imageTopicPosition =Offset.zero; + + void _calculateCardPosition() { + final RenderBox cardRenderBox = cardsKey.currentContext!.findRenderObject() as RenderBox; + final Offset cardPosition = cardRenderBox.localToGlobal(Offset.zero); + final Size cardSize = cardRenderBox.size; + + // Calculate the position for the image + final double imageX = cardPosition.dx + cardSize.width / 2; + final double imageY = cardPosition.dy + cardSize.height; + + setState(() { + _imageCardPosition = Offset(imageX, imageY); + }); + } + + 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) { + _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(); + _calculateCardPosition(); + 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.push('/marriages'); + }, + )..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.push('/marriages'); + }, + )..show(context: context); + } + + } + + void _initTarget(){ + homeTargets=[ + TargetFocus( + identify: 'cardTopicKey', + keyTarget: cardTopicKey, + shape: ShapeLightFocus.RRect, + contents: [ + createTargetContent( + text: AppLocalizations.of(context)!.home_topic, + alignment: ContentAlign.bottom, + gap: 45, + space: 40, + ), + TargetContent( + align: ContentAlign.bottom, + child: SizedBox( + width: double.infinity, + height: MediaQuery.of(context).size.height*0.69, + 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( + '1/7', + style: const TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + const SizedBox(height: 3), + Row( + 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( + 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.4, + top: _imageCardPosition.dy-29, + child: Image.asset( + 'assets/app_tour/down_right.png', + width: 40, + height: 90, + ), + ), + ] + ), + + ), + ), + ], + ), + TargetFocus( + identify: 'cardsKey', + keyTarget: cardsKey, + shape: ShapeLightFocus.RRect, + contents: [ + createTargetContent( + text:AppLocalizations.of(context)!.economy, + alignment: ContentAlign.bottom, + gap: 0, + space: 40,), + + 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: 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( + '2/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: () { + tutorialCoachMark.next(); + }, + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + 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/down_right.png', + width: 40, + height: 90, + ), + ), + ] + ), + + ), + ), + ], + ), + ]; + } + + void _initPreviousTarget(){ + previousHomeTargets=[ + TargetFocus( + identify: 'cardsKey', + keyTarget: cardsKey, + shape: ShapeLightFocus.RRect, + contents: [ + createTargetContent( + text:AppLocalizations.of(context)!.economy, + alignment: ContentAlign.bottom, + gap: 0, + space: 40,), + 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: 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( + '2/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( + 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; + tutorialCoachMark.finish(); + }, + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + 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/down_right.png', + width: 40, + height: 90, + ), + ), + ] + ), + + ), + ), + ], + ), + TargetFocus( + identify: 'cardTopicKey', + keyTarget: cardTopicKey, + shape: ShapeLightFocus.RRect, + contents: [ + createTargetContent( + text:AppLocalizations.of(context)!.home_topic, + alignment: ContentAlign.bottom, + gap: 45, + space: 40, + ), + TargetContent( + align: ContentAlign.bottom, + child: SizedBox( + width: double.infinity, + height: MediaQuery.of(context).size.height*0.69, + child: Stack( + children: [ + Positioned( + bottom: 35, + left: 16, + right: 16, + child: 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, + ), + ), + ), + Column( + children: [ + Text( + '1/7', + style: const TextStyle( + color: Colors.white, + fontSize: 14, + ), + ), + const SizedBox(height: 3), + 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( + 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.4, + top: _imageCardPosition.dy-29, + child: Image.asset( + 'assets/app_tour/down_right.png', + width: 40, + height: 90, + ), + ), + ] + ), + + ), + ), + ], + ), + ]; + } + + @override void initState() { super.initState(); fetchData(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _starTourRender(); + }); } Future fetchData() async { - const baseUrl = 'https://pb.venbait.in/api/getHomePageData'; + const baseUrl = 'https://pb.venbait.in/api/getHomePageData?language=en'; try { final response = await http.get(Uri.parse(baseUrl)); if (response.statusCode == 200) { @@ -336,6 +885,9 @@ class EconomyStatsState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ RoundedCornerContainer( + key: mainTopic['main_topic'] =='ECONOMY' + ? cardTopicKey + : null, text: mainTopic['main_topic'], backgroundColor: backgroundColor, textStyle: const TextStyle( @@ -347,6 +899,9 @@ class EconomyStatsState extends State { 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( diff --git a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart index 0d3e5450..1d13c05d 100644 --- a/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart +++ b/lib/presentation/routes/drawer_routes/custom_drawer_routes.dart @@ -6,9 +6,11 @@ import 'package:go_router/go_router.dart'; import 'package:pocketbase/pocketbase.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; +import 'package:tutorial_coach_mark/tutorial_coach_mark.dart'; -import '../../components/indicators/locale_provider.dart'; -import '../../components/my_toggle.dart'; +import 'package:uae_stat/presentation/Screens/app_tour/Target_content.dart'; +import 'package:uae_stat/presentation/components/indicators/locale_provider.dart'; +import 'package:uae_stat/presentation/components/my_toggle.dart'; class BaseScaffold extends ConsumerStatefulWidget { final Widget body; @@ -48,11 +50,447 @@ class _BaseScaffoldState extends ConsumerState { String? collectionIds; String? collectionId; + late TutorialCoachMark tutorialCoachMark; + late List appBarTargets; + final GlobalKey bottomNavKey = GlobalKey(); + final GlobalKey toggleKey = GlobalKey(); + final GlobalKey drawerKey = GlobalKey(); + late double toggleHeight=0; @override void initState() { super.initState(); _checkUserId(); + WidgetsBinding.instance.addPostFrameCallback((_) { + _startAppbarTour(); + }); + } + //Method to Start App Tour + void _startAppbarTour() { + final scaffoldProvider = ref.watch(scaffoldTourProvider); + _calculatetooglePosition(); + if (!scaffoldProvider) { + // Show Home Tour + _initTarget(); + tutorialCoachMark = TutorialCoachMark( + paddingFocus: 0, + useSafeArea: true, + pulseEnable: false, + hideSkip: true, + targets: appBarTargets, // Ensure `homeTargets` is defined for Home Tour + onFinish: () { + ref.read(homeTourProvider.notifier).state = true; + debugPrint('Home Tutorial Finished'); + }, + )..show(context: context); + } + + } + + void _calculatetooglePosition() { + final RenderBox cardRenderBox = toggleKey.currentContext!.findRenderObject() as RenderBox; + final Offset cardPosition = cardRenderBox.localToGlobal(Offset.zero); + final Size cardSize = cardRenderBox.size; + toggleHeight = cardSize.height; + } + + 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; + } + + void _initTarget(){ + final double screenWidth= MediaQuery.of(context).size.width; + final double screenHeight= MediaQuery.of(context).size.height; + appBarTargets=[ + TargetFocus( + identify: 'bottomNav', + keyTarget: bottomNavKey, + shape: ShapeLightFocus.RRect, // Rounded rectangle highlight + contents: [ + createTargetContent( + text: AppLocalizations.of(context)!.tour_navBar, + alignment: ContentAlign.top, + space: 0, + gap: 100, + + ), + TargetContent( + align: ContentAlign.top, + child: + SizedBox( + width: double.infinity, + height: MediaQuery.of(context).size.height* 0.30, // Set an appropriate height for the Stack + 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( + '5/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( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () { + tutorialCoachMark.finish(); + ref.read(scaffoldTourProvider.notifier).state=true; + ref.read(previousChartsTourProvider.notifier).state=false; + context.push('/marriages'); + }, + ), + ), + const SizedBox(width: 10), + Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Color(0xFF7DAFBC), + border: Border.all(color: Color(0xFF7DAFBC), width: 1.5), + ), + child: IconButton( + icon: const Icon(Icons.arrow_forward, color: Colors.white), + onPressed: () { + tutorialCoachMark.next(); + }, + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + TargetContent( + padding: EdgeInsets.only(left:screenWidth*0.4, bottom: 0 ), + align: ContentAlign.top, + child: Container( + width: 200, + height: 100, + child: Stack( + children: [ + Image.asset( + 'assets/app_tour/leftUp.png', + fit: BoxFit.contain, + ), + + ], + ), + ), + ), + ], + ), + TargetFocus( + identify: 'drawerKey', + keyTarget: drawerKey, + shape: ShapeLightFocus.RRect, + radius: 0, // No rounded corners + paddingFocus: 1, // Rounded rectangle highlight + contents: [ + createTargetContent( + text: AppLocalizations.of(context)!.mainMenu, + alignment: ContentAlign.bottom, + gap: 55, + space: 0, + ), + TargetContent( + align: ContentAlign.bottom, + child: + SizedBox( + width: double.infinity, + height: MediaQuery.of(context).size.height* 0.80, // Set an appropriate height for the Stack + 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( + '6/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( + 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( + icon: const Icon(Icons.arrow_forward, color: Colors.white), + onPressed: () { + tutorialCoachMark.next(); + }, + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + TargetContent( + padding: EdgeInsets.only(right: screenWidth*0.7, top: toggleHeight-5), + align: ContentAlign.right, + child: Container( + width: 200, + height: screenHeight/4, + 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: 'toggleKey', + keyTarget: toggleKey, + shape: ShapeLightFocus.RRect, + radius: 0, + paddingFocus: 0,// Rounded rectangle highlight + contents: [ + createTargetContent( + text: AppLocalizations.of(context)!.toggle, + space: 0, + alignment: ContentAlign.bottom, + gap: 50, + ), + TargetContent( + align: ContentAlign.bottom, + child: + SizedBox( + width: double.infinity, + height: MediaQuery.of(context).size.height* 0.80, + 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( + '7/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( + icon: const Icon(Icons.arrow_back, color: Colors.white), + onPressed: () { + tutorialCoachMark.previous(); + }, + ), + ), + const SizedBox(width: 10), + + ElevatedButton( + onPressed: () { + debugPrint('Got it 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; + tutorialCoachMark.finish(); + }, + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + side: const BorderSide(color: Colors.white), + backgroundColor: Colors.white, + elevation: 0, + ), + child: const Text( + 'Got it', + style: TextStyle( + color: Color(0xFF7DAFBC), + fontSize: 14, + ), + ), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ), + TargetContent( + padding: EdgeInsets.only(left:screenWidth*0.7, top: toggleHeight), + align: ContentAlign.left, + child: Container( + width: 200, + height: screenHeight/4, + child: Stack( + children: [ + Image.asset( + 'assets/app_tour/down_right.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, + // ), + // ), + // ), + ], + ), + ), + ), + ], + ), + ]; } // Method to retrieve userId from SharedPreferences @@ -221,7 +659,9 @@ class _BaseScaffoldState extends ConsumerState { // onPressed: () {}, // icon: const Icon(Icons.toggle_off_outlined), // ), - MyToggle(isOn: locale?.languageCode == 'en', + MyToggle( + key: toggleKey, + isOn: locale?.languageCode == 'en', knobTextWhenOn: 'ع', knobTextWhenOff: 'EN', pathColorWhenOn: Colors.grey.shade300, @@ -248,6 +688,7 @@ class _BaseScaffoldState extends ConsumerState { child: Builder( builder: (BuildContext context) { return IconButton( + key:drawerKey, color: (widget.showBackButton == true) ? Colors.white : Colors.black, @@ -351,6 +792,7 @@ class _BaseScaffoldState extends ConsumerState { ), ), bottomNavigationBar: BottomNavigationBar( + key: bottomNavKey, currentIndex: _getSelectedIndex(currentRoute), onTap: (index) => _onItemTapped(context, index), type: BottomNavigationBarType.fixed, diff --git a/pubspec.lock b/pubspec.lock index f310f720..be76549b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,31 +5,31 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "88399e291da5f7e889359681a8f64b18c5123e03576b01f32a6a276611e511c3" + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "78.0.0" + version: "72.0.0" _macros: dependency: transitive description: dart source: sdk - version: "0.3.3" + version: "0.3.2" analyzer: dependency: transitive description: name: analyzer - sha256: "62899ef43d0b962b056ed2ebac6b47ec76ffd003d5f7c4e4dc870afe63188e33" + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "7.1.0" + version: "6.7.0" analyzer_plugin: dependency: transitive description: name: analyzer_plugin - sha256: "1d460d14e3c2ae36dc2b32cef847c4479198cf87704f63c3c3c8150ee50c3916" + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" url: "https://pub.dev" source: hosted - version: "0.12.0" + version: "0.11.3" ansicolor: dependency: transitive description: @@ -74,50 +74,50 @@ packages: dependency: transitive description: name: build - sha256: cef23f1eda9b57566c81e2133d196f8e3df48f244b317368d65c5943d91148f0 + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.4.1" build_config: dependency: transitive description: name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" build_daemon: dependency: transitive description: name: build_daemon - sha256: "294a2edaf4814a378725bfe6358210196f5ea37af89ecd81bfa32960113d4948" + sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9" url: "https://pub.dev" source: hosted - version: "4.0.3" + version: "4.0.2" build_resolvers: dependency: transitive description: name: build_resolvers - sha256: "99d3980049739a985cf9b21f30881f46db3ebc62c5b8d5e60e27440876b1ba1e" + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.2" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "74691599a5bc750dc96a6b4bfd48f7d9d66453eab04c7f4063134800d6a5c573" + sha256: "028819cfb90051c6b5440c7e574d1896f8037e3c96cf17aaeb054c9311cfbf4d" url: "https://pub.dev" source: hosted - version: "2.4.14" + version: "2.4.13" build_runner_core: dependency: transitive description: name: build_runner_core - sha256: "22e3aa1c80e0ada3722fe5b63fd43d9c8990759d0a2cf489c8c5d7b2bdebc021" + sha256: f8126682b87a7282a339b871298cc12009cb67109cfa1614d6436fb0289193e0 url: "https://pub.dev" source: hosted - version: "8.0.0" + version: "7.3.2" built_collection: dependency: transitive description: @@ -186,10 +186,10 @@ packages: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.18.0" connectivity_plus: dependency: "direct main" description: @@ -258,42 +258,42 @@ packages: dependency: "direct dev" description: name: custom_lint - sha256: "6d509673c4dd0baa90e60dc8366bc2acc6690f16a7d44bfae31294d82c5d2a62" + sha256: "3486c470bb93313a9417f926c7dd694a2e349220992d7b9d14534dc49c15bba9" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.7.0" custom_lint_builder: dependency: transitive description: name: custom_lint_builder - sha256: "8cc525c7b160eb47bb1ded8b2633c0f8b907930eb986ac577aded87cdd2835fe" + sha256: "42cdc41994eeeddab0d7a722c7093ec52bd0761921eeb2cbdbf33d192a234759" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.7.0" custom_lint_core: dependency: transitive description: name: custom_lint_core - sha256: "6dcee8a017181941c51a110da7e267c1d104dc74bec8862eeb8c85b5c8759a9e" + sha256: "02450c3e45e2a6e8b26c4d16687596ab3c4644dd5792e3313aa9ceba5a49b7f5" url: "https://pub.dev" source: hosted - version: "0.7.1" + version: "0.7.0" custom_lint_visitor: dependency: transitive description: name: custom_lint_visitor - sha256: "14df0760dfa81b7b0c398c876045f4e4a343eb2c9d200c66163671dd3e337c1b" + sha256: "8aeb3b6ae2bb765e7716b93d1d10e8356d04e0ff6d7592de6ee04e0dd7d6587d" url: "https://pub.dev" source: hosted - version: "1.0.0+7.1.0" + version: "1.0.0+6.7.0" dart_style: dependency: transitive description: name: dart_style - sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" + sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "2.3.7" dbus: dependency: transitive description: @@ -565,10 +565,10 @@ packages: dependency: "direct dev" description: name: freezed - sha256: "59a584c24b3acdc5250bb856d0d3e9c0b798ed14a4af1ddb7dc1c7b41df91c9c" + sha256: "44c19278dd9d89292cf46e97dc0c1e52ce03275f40a97c5a348e802a924bf40e" url: "https://pub.dev" source: hosted - version: "2.5.8" + version: "2.5.7" freezed_annotation: dependency: "direct main" description: @@ -661,10 +661,10 @@ packages: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" image: dependency: transitive description: @@ -749,10 +749,10 @@ packages: dependency: "direct dev" description: name: injectable_generator - sha256: b04673a4c88b3a848c0c77bf58b8309f9b9e064d9fe1df5450c8ee1675eaea1a + sha256: af403d76c7b18b4217335e0075e950cd0579fd7f8d7bd47ee7c85ada31680ba1 url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.6.2" intl: dependency: "direct main" description: @@ -789,10 +789,10 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: b0a98230538fe5d0b60a22fb6bf1b6cb03471b53e3324ff6069c591679dd59c9 + sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c url: "https://pub.dev" source: hosted - version: "6.9.3" + version: "6.9.0" jwt_decoder: dependency: "direct main" description: @@ -805,18 +805,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.7" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.8" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -829,10 +829,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" logging: dependency: transitive description: @@ -845,10 +845,10 @@ packages: dependency: transitive description: name: macros - sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656" + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" url: "https://pub.dev" source: hosted - version: "0.1.3-main.0" + version: "0.1.2-main.4" mailer: dependency: "direct main" description: @@ -1045,10 +1045,10 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.4.0" recase: dependency: transitive description: @@ -1077,10 +1077,10 @@ packages: dependency: transitive description: name: riverpod_analyzer_utils - sha256: "837a6dc33f490706c7f4632c516bcd10804ee4d9ccc8046124ca56388715fdf3" + sha256: c6b8222b2b483cb87ae77ad147d6408f400c64f060df7a225b127f4afef4f8c8 url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.5.8" riverpod_annotation: dependency: "direct main" description: @@ -1093,18 +1093,18 @@ packages: dependency: "direct dev" description: name: riverpod_generator - sha256: "120d3310f687f43e7011bb213b90a436f1bbc300f0e4b251a72c39bccb017a4f" + sha256: "63546d70952015f0981361636bf8f356d9cfd9d7f6f0815e3c07789a41233188" url: "https://pub.dev" source: hosted - version: "2.6.4" + version: "2.6.3" riverpod_lint: dependency: "direct dev" description: name: riverpod_lint - sha256: b05408412b0f75dec954e032c855bc28349eeed2d2187f94519e1ddfdf8b3693 + sha256: "83e4caa337a9840469b7b9bd8c2351ce85abad80f570d84146911b32086fbd99" url: "https://pub.dev" source: hosted - version: "2.6.4" + version: "2.6.3" rxdart: dependency: transitive description: @@ -1196,10 +1196,10 @@ packages: dependency: transitive description: name: shelf - sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 url: "https://pub.dev" source: hosted - version: "1.4.2" + version: "1.4.1" shelf_web_socket: dependency: transitive description: @@ -1212,7 +1212,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" smart_api_client: dependency: "direct main" description: @@ -1224,10 +1224,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "1.5.0" source_helper: dependency: transitive description: @@ -1256,10 +1256,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.11.1" state_notifier: dependency: transitive description: @@ -1288,10 +1288,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.2.0" syncfusion_flutter_charts: dependency: "direct main" description: @@ -1320,10 +1320,10 @@ packages: dependency: transitive description: name: test_api - sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.3" + version: "0.7.2" the_validator: dependency: "direct main" description: @@ -1340,6 +1340,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: @@ -1408,10 +1416,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9" + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.3.3" url_launcher_windows: dependency: transitive description: @@ -1448,10 +1456,10 @@ packages: dependency: transitive description: name: video_player_android - sha256: "7018dbcb395e2bca0b9a898e73989e67c0c4a5db269528e1b036ca38bcca0d0b" + sha256: "391e092ba4abe2f93b3e625bd6b6a6ec7d7414279462c1c0ee42b5ab8d0a0898" url: "https://pub.dev" source: hosted - version: "2.7.17" + version: "2.7.16" video_player_avfoundation: dependency: transitive description: @@ -1480,10 +1488,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.3.0" + version: "14.2.5" watcher: dependency: transitive description: @@ -1581,5 +1589,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.6.0 <4.0.0" - flutter: ">=3.27.0" + dart: ">=3.5.2 <4.0.0" + flutter: ">=3.24.0" diff --git a/pubspec.yaml b/pubspec.yaml index 0145a0ed..6caf3e5f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: cupertino_icons: ^1.0.6 fl_chart: ^0.69.2 - + tutorial_coach_mark: ^1.2.12 marquee: ^2.2.3 url_launcher: ^6.3.1 # dynamic_layouts: @@ -110,6 +110,7 @@ flutter: - assets/cached_responses/sharepoint/country_leader_imgs/ - assets/edit_profile/ - assets/splash_screen/ + - assets/app_tour/ fonts: - family: Segoe