From c35c38aeb14ce5e0437d9acf9d48f91101f811fa Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 24 Sep 2025 13:33:28 +0300 Subject: [PATCH] onboarding screen added. --- lib/core/location_util.dart | 2 +- .../onboarding/onboarding_screen.dart | 162 ++++++++++++++++++ .../onboarding}/splash_animation_screen.dart | 3 +- lib/splashPage.dart | 7 +- lib/widgets/buttons/custom_button.dart | 6 + 5 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 lib/presentation/onboarding/onboarding_screen.dart rename lib/{ => presentation/onboarding}/splash_animation_screen.dart (98%) diff --git a/lib/core/location_util.dart b/lib/core/location_util.dart index ecfab62..bd35dcb 100644 --- a/lib/core/location_util.dart +++ b/lib/core/location_util.dart @@ -233,7 +233,7 @@ class LocationUtils { HmsLocation.Location data = await locationService.getLastLocation(); if (data.latitude == null || data.longitude == null) { - appState.resetLocation(); + appState.resetLocation(); HmsLocation.LocationRequest request = HmsLocation.LocationRequest() ..priority = HmsLocation.LocationRequest.PRIORITY_HIGH_ACCURACY ..interval = 1000 // 1 second diff --git a/lib/presentation/onboarding/onboarding_screen.dart b/lib/presentation/onboarding/onboarding_screen.dart new file mode 100644 index 0000000..1acb867 --- /dev/null +++ b/lib/presentation/onboarding/onboarding_screen.dart @@ -0,0 +1,162 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; +import 'package:lottie/lottie.dart'; + +class OnboardingScreen extends StatefulWidget { + OnboardingScreen({Key? key}) : super(key: key); + + @override + _OnboardingScreenState createState() { + return _OnboardingScreenState(); + } +} + +class _OnboardingScreenState extends State { + int selectedIndex = 0; + + late PageController pageController; + + void goToHomePage() => Navigator.of(context).pushReplacement(FadePage(page: LandingNavigation())); + + @override + void initState() { + super.initState(); + pageController = PageController(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: AppColors.whiteColor, + body: SafeArea( + top: false, + left: false, + right: false, + child: Column( + spacing: 24, + children: [ + PageView( + controller: pageController, + children: [ + onboardingView( + AppAnimations.onboarding_1, + "Booking appointment has never been easy".needTranslation, + "In few clicks find yourself having consultation with the doctor of your choice.".needTranslation, + ), + onboardingView( + AppAnimations.onboarding_2, + "Access the medical history on finger tips".needTranslation, + "Keep track on your medical history including labs, prescription, insurance, etc".needTranslation, + ), + ], + onPageChanged: (int index) { + selectedIndex = index; + setState(() {}); + }, + ).expanded, + Row( + spacing: 4.h, + children: [ + AnimatedContainer( + duration: const Duration(milliseconds: 250), + height: 6.h, + width: selectedIndex == 0 ? 18.h : 6.h, + decoration: BoxDecoration(color: selectedIndex == 0 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), + ), + AnimatedContainer( + duration: const Duration(milliseconds: 250), + height: 6.h, + width: selectedIndex == 1 ? 18.h : 6.h, + decoration: BoxDecoration(color: selectedIndex == 1 ? AppColors.textColor : AppColors.inputLabelTextColor, borderRadius: BorderRadius.circular(30)), + ), + ], + ).paddingOnly(left: 24, right: 24), + Row( + children: [ + AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + transitionBuilder: (child, anim) => FadeTransition(opacity: anim, child: child), + child: selectedIndex == 0 + ? CustomButton( + text: "Skip".needTranslation, + onPressed: () => goToHomePage(), + width: 86, + backgroundColor: Color(0xffFEE9EA), + textColor: AppColors.primaryRedColor, + borderWidth: 0, + borderColor: Colors.transparent, + ).paddingOnly(left: 24) + : const SizedBox.shrink(), + ), + const Spacer(), + AnimatedContainer( + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOut, + width: selectedIndex == 0 ? 86 : MediaQuery.of(context).size.width - 48, + margin: EdgeInsets.only(left: 24, right: 24), + decoration: BoxDecoration( + color: AppColors.primaryRedColor, + borderRadius: BorderRadius.circular(12), + ), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 250), + transitionBuilder: (child, anim) => FadeTransition(opacity: anim, child: child), + child: selectedIndex == 0 + ? CustomButton( + icon: AppAssets.arrow_forward, + width: 86, + text: "".needTranslation, + backgroundColor: Colors.transparent, + onPressed: () { + pageController.animateToPage(1, duration: Duration(milliseconds: 400), curve: Curves.easeInOut); + }) + : CustomButton( + text: "Get Started".needTranslation, + fontWeight: FontWeight.w500, + fontSize: 16, + textOverflow: TextOverflow.ellipsis, + backgroundColor: Colors.transparent, + onPressed: () => goToHomePage(), + ), + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget onboardingView(String icon, String heading, String body) { + return Column( + mainAxisAlignment: MainAxisAlignment.end, + spacing: 12, + children: [ + Lottie.asset(icon, repeat: true, reverse: false, frameRate: FrameRate(60), width: MediaQuery.sizeOf(context).width - 50, height: MediaQuery.sizeOf(context).width - 50), + 12.height, + Text( + heading, + style: TextStyle(fontSize: 36, fontWeight: FontWeight.w600, color: AppColors.textColor, letterSpacing: -0.4, height: 1), + ), + Text( + body, + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: AppColors.greyTextColor, letterSpacing: 0, height: 26 / 16), + ), + ], + ).paddingOnly(left: 24, right: 24); + } +} diff --git a/lib/splash_animation_screen.dart b/lib/presentation/onboarding/splash_animation_screen.dart similarity index 98% rename from lib/splash_animation_screen.dart rename to lib/presentation/onboarding/splash_animation_screen.dart index 0122d49..1010013 100644 --- a/lib/splash_animation_screen.dart +++ b/lib/presentation/onboarding/splash_animation_screen.dart @@ -5,6 +5,7 @@ import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/presentation/authentication/login.dart'; +import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:lottie/lottie.dart'; @@ -29,7 +30,7 @@ class _SplashAnimationScreenState extends State with Sing _controller = AnimationController(vsync: this); _controller.addListener(() { if (_controller.status == AnimationStatus.completed) { - Navigator.of(context).pushReplacement(FadePage(page: widget.routeWidget ?? LoginScreen())); + Navigator.of(context).pushReplacement(FadePage(page: widget.routeWidget ?? LandingNavigation())); } }); } diff --git a/lib/splashPage.dart b/lib/splashPage.dart index bba217b..0470e03 100644 --- a/lib/splashPage.dart +++ b/lib/splashPage.dart @@ -6,7 +6,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_zoom_videosdk/native/zoom_videosdk.dart'; -import 'package:hmg_patient_app_new/splash_animation_screen.dart'; +import 'package:hmg_patient_app_new/presentation/onboarding/onboarding_screen.dart'; +import 'package:hmg_patient_app_new/presentation/onboarding/splash_animation_screen.dart'; import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_export.dart'; @@ -35,7 +36,7 @@ class SplashPage extends StatefulWidget { class _SplashScreenState extends State { late AuthenticationViewModel authVm; - bool isNewDesign = false; + bool isNewDesign = true; Future initializeStuff() async { Timer( @@ -50,7 +51,7 @@ class _SplashScreenState extends State { LocalNotification.init(onNotificationClick: (payload) {}); if (isNewDesign) { - Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen())); + Navigator.of(context).pushReplacement(FadePage(page: SplashAnimationScreen(routeWidget: OnboardingScreen()))); } else { Navigator.of(context).pushReplacement( CustomPageRoute( diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index d0b4a6c..5331e43 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -20,7 +20,9 @@ class CustomButton extends StatelessWidget { final bool isDisabled; final Color? iconColor; final double height; + final double? width; final double iconSize; + final TextOverflow? textOverflow; CustomButton({ Key? key, @@ -39,7 +41,9 @@ class CustomButton extends StatelessWidget { this.icon, this.iconColor = Colors.white, this.height = 56, + this.width, this.iconSize = 24, + this.textOverflow, }) : super(key: key); @override @@ -48,6 +52,7 @@ class CustomButton extends StatelessWidget { onTap: isDisabled ? null : onPressed, child: Container( height: height, + width: width, padding: padding, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: isDisabled ? Colors.transparent : backgroundColor, @@ -69,6 +74,7 @@ class CustomButton extends StatelessWidget { padding: EdgeInsets.only(top: 2.5), child: Text( text, + overflow: textOverflow, style: context.dynamicTextStyle( fontSize: fontSize.fSize, color: isDisabled ? textColor.withOpacity(0.5) : textColor,