diff --git a/assets/images/svg/accessibility.svg b/assets/images/svg/accessibility.svg new file mode 100644 index 0000000..fbe3d12 --- /dev/null +++ b/assets/images/svg/accessibility.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/arrow_forward.svg b/assets/images/svg/arrow_forward.svg new file mode 100644 index 0000000..2638c5b --- /dev/null +++ b/assets/images/svg/arrow_forward.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/call_fill.svg b/assets/images/svg/call_fill.svg new file mode 100644 index 0000000..25509ed --- /dev/null +++ b/assets/images/svg/call_fill.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/email_transparent.svg b/assets/images/svg/email_transparent.svg new file mode 100644 index 0000000..6f45a86 --- /dev/null +++ b/assets/images/svg/email_transparent.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/emergency.svg b/assets/images/svg/emergency.svg new file mode 100644 index 0000000..7a5a6e3 --- /dev/null +++ b/assets/images/svg/emergency.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/external_link.svg b/assets/images/svg/external_link.svg new file mode 100644 index 0000000..822ebc2 --- /dev/null +++ b/assets/images/svg/external_link.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/language_change.svg b/assets/images/svg/language_change.svg new file mode 100644 index 0000000..e84f0c3 --- /dev/null +++ b/assets/images/svg/language_change.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/images/svg/minus.svg b/assets/images/svg/minus.svg new file mode 100644 index 0000000..01d8ebd --- /dev/null +++ b/assets/images/svg/minus.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/my_address.svg b/assets/images/svg/my_address.svg new file mode 100644 index 0000000..59faec9 --- /dev/null +++ b/assets/images/svg/my_address.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/permission.svg b/assets/images/svg/permission.svg new file mode 100644 index 0000000..00d6f5c --- /dev/null +++ b/assets/images/svg/permission.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/privacy_terms.svg b/assets/images/svg/privacy_terms.svg new file mode 100644 index 0000000..ee79318 --- /dev/null +++ b/assets/images/svg/privacy_terms.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/svg/rate.svg b/assets/images/svg/rate.svg new file mode 100644 index 0000000..bfdaf3f --- /dev/null +++ b/assets/images/svg/rate.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/smart_phone_fill.svg b/assets/images/svg/smart_phone_fill.svg new file mode 100644 index 0000000..70beb7f --- /dev/null +++ b/assets/images/svg/smart_phone_fill.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/svg/touch_face_id.svg b/assets/images/svg/touch_face_id.svg new file mode 100644 index 0000000..07808bd --- /dev/null +++ b/assets/images/svg/touch_face_id.svg @@ -0,0 +1,4 @@ + + + + diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 4a8a3da..0a414d0 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -4,6 +4,8 @@ class AppAssets { static const String hmg = '$svgBasePath/hmg.svg'; static const String arrow_back = '$svgBasePath/arrow-back.svg'; + static const String arrow_forward = '$svgBasePath/arrow_forward.svg'; + static const String externalLink = '$svgBasePath/external_link.svg'; static const String calendar = '$svgBasePath/calendar.svg'; static const String hmc = '$svgBasePath/hmc.svg'; static const String ksa = '$svgBasePath/ksa.svg'; @@ -117,6 +119,18 @@ class AppAssets { static const String free_med_delivery_icon = '$svgBasePath/free_med_delivery_icon.svg'; static const String livecare_book_icon = '$svgBasePath/livecare_book_icon.svg'; static const String doctor_profile_icon = '$svgBasePath/doctor_profile_icon.svg'; + static const String accessibility = '$svgBasePath/accessibility.svg'; + static const String call_fill = '$svgBasePath/call_fill.svg'; + static const String email_transparent = '$svgBasePath/email_transparent.svg'; + static const String emergency = '$svgBasePath/emergency.svg'; + static const String language_change = '$svgBasePath/language_change.svg'; + static const String my_address = '$svgBasePath/my_address.svg'; + static const String permission = '$svgBasePath/permission.svg'; + static const String privacy_terms = '$svgBasePath/privacy_terms.svg'; + static const String rate = '$svgBasePath/rate.svg'; + static const String smart_phone_fill = '$svgBasePath/smart_phone_fill.svg'; + static const String touch_face_id = '$svgBasePath/touch_face_id.svg'; + static const String minus = '$svgBasePath/minus.svg'; //bottom navigation// static const String homeBottom = '$svgBasePath/home_bottom.svg'; @@ -127,6 +141,7 @@ class AppAssets { static const String closeBottomNav = '$svgBasePath/close_bottom_nav.svg'; static const String feedback = '$svgBasePath/feedback.svg'; static const String news = '$svgBasePath/news.svg'; + // PNGS // static const String hmg_logo = '$pngBasePath/hmg_logo.png'; static const String livecare_service = '$pngBasePath/livecare_service.png'; @@ -148,5 +163,4 @@ class AppAnimations { static const String loadingAnimation = '$lottieBasePath/Loader.json'; static const String errorAnimation = '$lottieBasePath/ErrorAnimation.json'; static const String warningAnimation = '$lottieBasePath/warningAnimation.json'; - } diff --git a/lib/core/utils/calendar_utils.dart b/lib/core/utils/calendar_utils.dart index 979ce1b..2068db9 100644 --- a/lib/core/utils/calendar_utils.dart +++ b/lib/core/utils/calendar_utils.dart @@ -285,11 +285,11 @@ Future checkAndRemove(hasReminder, {bool delete = false, String itemDescri if (calendarUtils.calendars != null) { if (Platform.isAndroid) { - await processEvents(calendarUtils.calendars, calendarUtils, params, delete, itemDescriptionN,hasReminder); + await processEvents(calendarUtils.calendars, calendarUtils, params, delete, itemDescriptionN, hasReminder); } else { List? iosCalendars = await _myPlugin.getCalendars(); if (iosCalendars != null) { - await processEvents(iosCalendars.map((cal) => Calendar(id: cal.id, name: cal.name, accountName: cal.accountName)).toList(), calendarUtils, params, delete, itemDescriptionN,hasReminder); + await processEvents(iosCalendars.map((cal) => Calendar(id: cal.id, name: cal.name, accountName: cal.accountName)).toList(), calendarUtils, params, delete, itemDescriptionN, hasReminder); } } } diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index d34a44c..152cd54 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -650,6 +650,24 @@ class Utils { ); } + static Widget getPaymentAmountWithSymbol2(num habibWalletAmount, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) { + return RichText( + text: TextSpan( + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.baseline, + baseline: TextBaseline.alphabetic, + child: Utils.buildSvgWithAssets(icon: AppAssets.saudi_riyal_icon, width: 14.h, height: 14.h, iconColor: Colors.black.withValues(alpha: 0.31)), + ), + TextSpan( + text: " $habibWalletAmount", + style: TextStyle(color: AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -4, fontWeight: FontWeight.w600, height: 1), + ), + ], + ), + ); + } + static Future isGoogleServicesAvailable() async { GooglePlayServicesAvailability availability = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability(); String status = availability.toString().split('.').last; diff --git a/lib/extensions/int_extensions.dart b/lib/extensions/int_extensions.dart index 4a2e477..80b3171 100644 --- a/lib/extensions/int_extensions.dart +++ b/lib/extensions/int_extensions.dart @@ -6,9 +6,7 @@ extension IntExtensions on int { Widget get width => SizedBox(width: toDouble()); - Widget get divider => Divider(height: toDouble(), thickness: toDouble(), color: AppColors.buttonColor); + Widget get divider => Divider(height: toDouble(), thickness: toDouble(), color: Color(0x30D2D2D2)); Widget get makeItSquare => SizedBox(width: toDouble(), height: toDouble()); - - } diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index 02e99f1..7140c05 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -132,14 +132,26 @@ extension EmailValidator on String { decoration: isUnderLine ? TextDecoration.underline : null), ); - Widget toText14({Color? color, bool isUnderLine = false, bool isBold = false, bool isCenter = false, FontWeight? weight, int? maxlines, double? letterSpacing = -1}) => Text( + Widget toText14( + {Color? color, + bool isUnderLine = false, + bool isBold = false, + bool isCenter = false, + FontWeight? weight, + int? maxlines, + double? letterSpacing = -1, + double? height, + TextOverflow? textOverflow}) => + Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: maxlines, + overflow: textOverflow, style: TextStyle( color: color ?? AppColors.blackColor, fontSize: 14.fSize, letterSpacing: letterSpacing, + height: height, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), decoration: isUnderLine ? TextDecoration.underline : null), ); @@ -162,8 +174,10 @@ extension EmailValidator on String { bool isBold = false, bool isCenter = false, int? maxlines, + double? height, TextAlign? textAlign, FontWeight? weight, + TextOverflow? textOverflow, double? letterSpacing = -0.4, }) => Text( @@ -174,6 +188,8 @@ extension EmailValidator on String { color: color ?? AppColors.blackColor, fontSize: 16.fSize, letterSpacing: letterSpacing, + height: height, + overflow: textOverflow, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), decoration: isUnderLine ? TextDecoration.underline : null, ), diff --git a/lib/presentation/habib_wallet/habib_wallet_page.dart b/lib/presentation/habib_wallet/habib_wallet_page.dart index 1d24d05..1be3e12 100644 --- a/lib/presentation/habib_wallet/habib_wallet_page.dart +++ b/lib/presentation/habib_wallet/habib_wallet_page.dart @@ -51,6 +51,7 @@ class _HabibWalletState extends State { children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -58,7 +59,7 @@ class _HabibWalletState extends State { "${_appState.getAuthenticatedUser()!.firstName!} ${_appState.getAuthenticatedUser()!.lastName!}".toText19(isBold: true, color: AppColors.whiteColor), "MRN: ${_appState.getAuthenticatedUser()!.patientId!}".toText14(weight: FontWeight.w500, color: AppColors.greyTextColor), ], - ), + ).expanded, Utils.buildSvgWithAssets(icon: AppAssets.habiblogo, width: 24.h, height: 24.h), ], ), diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 9f8ae79..f8cb8fd 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -34,6 +34,7 @@ import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart' show CustomTabBar; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/routes/spring_page_route_builder.dart'; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -86,227 +87,219 @@ class _LandingPageState extends State { return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: SingleChildScrollView( + padding: EdgeInsets.only(top: kToolbarHeight + 12.h, bottom: 24), child: Column( + spacing: 16.h, children: [ - Padding( - padding: EdgeInsets.only(top: 50.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - - appState.isAuthenticated - ? WelcomeWidget( - onTap: () { + Row( + spacing: 8.h, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + appState.isAuthenticated + ? WelcomeWidget( + onTap: () { + Navigator.of(context).push(springPageRoute(ProfileSettings())); + }, + name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), + imageUrl: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, + ).expanded + : CustomButton( + text: LocaleKeys.loginOrRegister.tr(context: context), + onPressed: () async { + await authVM.onLoginPressed(); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 16, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 50, + ), + Row( + mainAxisSize: MainAxisSize.min, + spacing: 12.h, + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 20, width: 20).onPress(() { Navigator.of(context).push( FadePage( - page: ProfileSettings(), + page: MedicalFilePage(), + // page: LoginScreen(), ), ); - }, - name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'), - imageUrl: appState - .getAuthenticatedUser() - ?.gender == 1 ? AppAssets.male_img : AppAssets.femaleImg, - ) - : CustomButton( - text: LocaleKeys.loginOrRegister.tr(context: context), - onPressed: () async { - await authVM.onLoginPressed(); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 16, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 50, - ), - Row( + }), + Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 20, width: 20).onPress(() { + Navigator.of(context).push( + FadePage( + page: MedicalFilePage(), + // page: LoginScreen(), + ), + ); + }), + Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 20, width: 20).onPress(() { + Navigator.of(context).push( + FadePage( + page: MedicalFilePage(), + // page: LoginScreen(), + ), + ); + }), + ], + ) + ], + ).paddingSymmetrical(24.h, 0.h), + appState.isAuthenticated + ? Column( children: [ - Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 20, width: 20).onPress(() { - Navigator.of(context).push( - FadePage( - page: MedicalFilePage(), - // page: LoginScreen(), + Container( + width: double.infinity, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, + ), + child: Padding( + padding: EdgeInsets.all(12.h), + child: Column( + children: [ + Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), + SizedBox(height: 12.h), + "You do not have any upcoming appointment. Please book an appointment".toText12(isCenter: true), + SizedBox(height: 12.h), + CustomButton( + text: LocaleKeys.bookAppo.tr(context: context), + onPressed: () { + Navigator.of(context).push( + FadePage( + page: BookAppointmentPage(), + ), + ); + }, + backgroundColor: Color(0xffFEE9EA), + borderColor: Color(0xffFEE9EA), + textColor: Color(0xffED1C2B), + fontSize: 14, + fontWeight: FontWeight.w500, + borderRadius: 12, + padding: EdgeInsets.fromLTRB(10, 0, 10, 0), + height: 40, + icon: AppAssets.add_icon, + iconColor: AppColors.primaryRedColor, + ), + ], ), - ); - }).paddingSymmetrical(8.h, 0), - Utils.buildSvgWithAssets(icon: AppAssets.search_icon, height: 20, width: 20).onPress(() { - Navigator.of(context).push( - FadePage( - page: MedicalFilePage(), - // page: LoginScreen(), + ), + ).paddingSymmetrical(24.h, 0.h), + SizedBox(height: 12.h), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + "Quick Links".toText16(isBold: true), + Row( + children: [ + "View medical file".toText12(color: AppColors.primaryRedColor), + SizedBox(width: 2.h), + Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), + ], ), - ); - }).paddingSymmetrical(8.h, 0), - Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 20, width: 20).onPress(() { + ], + ).paddingSymmetrical(24.h, 0.h).onPress(() { Navigator.of(context).push( FadePage( page: MedicalFilePage(), - // page: LoginScreen(), ), ); - }).paddingSymmetrical(8.h, 0), - ], - ) - ], - ).paddingSymmetrical(24.h, 0.h), - ), - SizedBox(height: 16.h), - appState.isAuthenticated - ? Column( - children: [ - Container( - width: double.infinity, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Padding( - padding: EdgeInsets.all(12.h), - child: Column( - children: [ - Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h), - SizedBox(height: 12.h), - "You do not have any upcoming appointment. Please book an appointment".toText12(isCenter: true), - SizedBox(height: 12.h), - CustomButton( - text: LocaleKeys.bookAppo.tr(context: context), - onPressed: () { - Navigator.of(context) - .push( - FadePage( - page: BookAppointmentPage(), - ), - ); - }, - backgroundColor: Color(0xffFEE9EA), - borderColor: Color(0xffFEE9EA), - textColor: Color(0xffED1C2B), - fontSize: 14, - fontWeight: FontWeight.w500, - borderRadius: 12, - padding: EdgeInsets.fromLTRB(10, 0, 10, 0), - height: 40, - icon: AppAssets.add_icon, - iconColor: AppColors.primaryRedColor, + }), + SizedBox(height: 12.h), + Container( + height: 127.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 12.h), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - "Quick Links".toText16(isBold: true), - Row( - children: [ - "View medical file".toText12(color: AppColors.primaryRedColor), - SizedBox(width: 2.h), - Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 10.h), - ], - ), - ], - ).paddingSymmetrical(24.h, 0.h).onPress(() { - Navigator.of(context).push( - FadePage( - page: MedicalFilePage(), - ), - ); - }), - SizedBox(height: 12.h), - Container( - height: 127.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - icon: LandingPageData.getLoggedInServiceCardsList[index].icon, - title: LandingPageData.getLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, - serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, - ), - ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: const EdgeInsets.only(left: 0, right: 8), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + icon: LandingPageData.getLoggedInServiceCardsList[index].icon, + title: LandingPageData.getLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getLoggedInServiceCardsList[index].isBold, + serviceName: LandingPageData.getLoggedInServiceCardsList[index].serviceName, + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 0.width, ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, + ), + ], ), ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h), - ], - ) + ).paddingSymmetrical(24.h, 0.h), + ], + ) : Container( - height: 127.h, - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.whiteColor, - borderRadius: 24, - ), - child: Padding( - padding: EdgeInsets.all(16.h), - child: Column( - children: [ - Expanded( - child: ListView.separated( - scrollDirection: Axis.horizontal, - itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, - shrinkWrap: true, - padding: const EdgeInsets.only(left: 0, right: 8), - itemBuilder: (context, index) { - return AnimationConfiguration.staggeredList( - position: index, - duration: const Duration(milliseconds: 1000), - child: SlideAnimation( - horizontalOffset: 100.0, - child: FadeInAnimation( - child: SmallServiceCard( - icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, - title: LandingPageData.getNotLoggedInServiceCardsList[index].title, - subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, - iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, - textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, - backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, - isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, - ), - ), + height: 127.h, + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24, + ), + child: Padding( + padding: EdgeInsets.all(16.h), + child: Column( + children: [ + Expanded( + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: LandingPageData.getNotLoggedInServiceCardsList.length, + shrinkWrap: true, + padding: const EdgeInsets.only(left: 0, right: 8), + itemBuilder: (context, index) { + return AnimationConfiguration.staggeredList( + position: index, + duration: const Duration(milliseconds: 1000), + child: SlideAnimation( + horizontalOffset: 100.0, + child: FadeInAnimation( + child: SmallServiceCard( + icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon, + title: LandingPageData.getNotLoggedInServiceCardsList[index].title, + subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle, + iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor, + textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor, + backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor, + isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold, + ), + ), + ), + ); + }, + separatorBuilder: (BuildContext cxt, int index) => 0.width, ), - ); - }, - separatorBuilder: (BuildContext cxt, int index) => 0.width, + ), + ], ), ), - ], - ), - ), - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 16.h), + ).paddingSymmetrical(24.h, 0.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -320,7 +313,6 @@ class _LandingPageState extends State { ), ], ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 16.h), SizedBox( height: 325.h, child: Column( @@ -354,9 +346,7 @@ class _LandingPageState extends State { ], ), ), - SizedBox(height: 16.h), appState.isAuthenticated ? HabibWalletCard() : SizedBox(), - SizedBox(height: 16.h), ], ), ), @@ -375,7 +365,7 @@ class _LandingPageState extends State { // sharedPref.setBool(HAS_ENABLED_QUICK_LOGIN, true); authVM.loginWithFingerPrintFace(() { isDone = true; - cacheService.saveBool(key: CacheConst.quickLoginEnabled,value: true); + cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: true); setState(() {}); }); }, diff --git a/lib/presentation/home/widgets/welcome_widget.dart b/lib/presentation/home/widgets/welcome_widget.dart index d761a7f..4baee01 100644 --- a/lib/presentation/home/widgets/welcome_widget.dart +++ b/lib/presentation/home/widgets/welcome_widget.dart @@ -1,7 +1,9 @@ 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/core/utils/utils.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/theme/colors.dart'; class WelcomeWidget extends StatelessWidget { @@ -23,32 +25,25 @@ class WelcomeWidget extends StatelessWidget { borderRadius: BorderRadius.circular(30), child: Row( mainAxisSize: MainAxisSize.min, + spacing: 8.h, children: [ - // Profile image - Image.asset(imageUrl, width: 40, height: 40), - - const SizedBox(width: 10), - - // Text column Column( crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4.h, + mainAxisSize: MainAxisSize.min, children: [ - - "Welcome".toText14(color: AppColors.greyTextColor), - - + "Welcome".needTranslation.toText14(color: AppColors.greyTextColor, height: 1, weight: FontWeight.w500), Row( + spacing: 4.h, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - - name.toText16(isBold: true), - - const SizedBox(width: 4), + name.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1, height: 1).expanded, const Icon(Icons.keyboard_arrow_down, size: 20, color: Colors.black), ], ), ], - ), + ).expanded, ], ), ); diff --git a/lib/presentation/lab/collapsing_list_view.dart b/lib/presentation/lab/collapsing_list_view.dart index f84872d..8f63f76 100644 --- a/lib/presentation/lab/collapsing_list_view.dart +++ b/lib/presentation/lab/collapsing_list_view.dart @@ -41,11 +41,14 @@ class CollapsingListView extends StatelessWidget { systemOverlayStyle: SystemUiOverlayStyle(statusBarBrightness: Brightness.light), surfaceTintColor: Colors.transparent, backgroundColor: AppColors.bgScaffoldColor, - leading: isLeading ? IconButton( - icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 32.h, height: 32.h), - padding: EdgeInsets.only(left: 12), - onPressed: () => Navigator.pop(context), - ) : SizedBox.shrink(), + leading: isLeading + ? IconButton( + icon: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.closeBottomNav : AppAssets.arrow_back, width: 32.h, height: 32.h), + padding: EdgeInsets.only(left: 12), + onPressed: () => Navigator.pop(context), + highlightColor: Colors.transparent, + ) + : SizedBox.shrink(), flexibleSpace: LayoutBuilder( builder: (context, constraints) { final double maxHeight = 100; diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index baccbe2..832031a 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -1,15 +1,24 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_swiper_view/flutter_swiper_view.dart'; 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/core/utils/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/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_view_model.dart'; +import 'package:hmg_patient_app_new/presentation/habib_wallet/habib_wallet_page.dart'; +import 'package:hmg_patient_app_new/presentation/habib_wallet/recharge_wallet_page.dart'; import 'package:hmg_patient_app_new/presentation/lab/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/app_language_change.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; +import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; +import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; class ProfileSettings extends StatefulWidget { @@ -70,7 +79,7 @@ class _ProfileSettingsState extends State { }, ), GridView( - gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 105 / 105, crossAxisSpacing: 9, mainAxisSpacing: 9), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, crossAxisSpacing: 9, mainAxisSpacing: 9), physics: const NeverScrollableScrollPhysics(), padding: const EdgeInsets.all(24), shrinkWrap: true, @@ -83,17 +92,103 @@ class _ProfileSettingsState extends State { hasShadow: true, ), child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 4.h, children: [ Row( + spacing: 8.h, + crossAxisAlignment: CrossAxisAlignment.center, children: [ Utils.buildSvgWithAssets(icon: AppAssets.wallet, width: 40.h, height: 40.h), + "Al Habib Wallet".needTranslation.toText14(weight: FontWeight.w600, maxlines: 2).expanded, + Utils.buildSvgWithAssets(icon: AppAssets.arrow_forward), ], - ) + ), + Spacer(), + Consumer(builder: (context, habibWalletVM, child) { + return Utils.getPaymentAmountWithSymbol2(habibWalletVM.habibWalletAmount, AppColors.whiteColor, 13.h, isExpanded: false) + .toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 24.h); + }), + CustomButton( + height: 40.h, + icon: AppAssets.recharge_icon, + iconSize: 24.h, + iconColor: AppColors.infoColor, + textColor: AppColors.infoColor, + text: "Recharge", + borderWidth: 0.h, + fontWeight: FontWeight.w500, + borderColor: Colors.transparent, + backgroundColor: Color(0xff45A2F8).withValues(alpha: 0.08), + padding: EdgeInsets.all(8.h), + fontSize: 14, + onPressed: () { + Navigator.of(context).push(CustomPageRoute(page: RechargeWalletPage())); + }, + ), ], - ), + ).onPress(() { + Navigator.of(context).push(CustomPageRoute(page: HabibWalletPage())); + }), ) ], - ) + ), + "Quick Actions".needTranslation.toText18(weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24, right: 24), + Container( + margin: EdgeInsets.only(left: 24, right: 24, top: 16, bottom: 24), + padding: EdgeInsets.only(top: 4, bottom: 4), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: Column( + children: [ + actionItem(AppAssets.language_change, "Language".needTranslation, () { + showCommonBottomSheetWithoutHeight(context, title: "Application Language".needTranslation, child: AppLanguageChange(), callBackFunc: () {}, isFullScreen: false); + }, trailingLabel: Utils.appState.isArabic() ? "العربية".needTranslation : "English".needTranslation), + 1.divider, + actionItem(AppAssets.accessibility, "Accessibility".needTranslation, () {}), + 1.divider, + actionItem(AppAssets.bell, "Notifications Settings".needTranslation, () {}), + 1.divider, + actionItem(AppAssets.touch_face_id, "Touch ID / Face ID Services".needTranslation, () {}, switchValue: true), + ], + ), + ), + "Personal Information".needTranslation.toText18(weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24, right: 24), + Container( + margin: EdgeInsets.only(left: 24, right: 24, top: 16, bottom: 24), + padding: EdgeInsets.only(top: 4, bottom: 4), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: Column( + children: [ + actionItem(AppAssets.email_transparent, "Update Email Address".needTranslation, () {}), + 1.divider, + actionItem(AppAssets.smart_phone_fill, "Phone Number".needTranslation, () {}), + 1.divider, + actionItem(AppAssets.my_address, "My Addresses".needTranslation, () {}), + 1.divider, + actionItem(AppAssets.emergency, "Emergency Contact".needTranslation, () {}), + ], + ), + ), + "Help & Support".needTranslation.toText18(weight: FontWeight.w600, textOverflow: TextOverflow.ellipsis, maxlines: 1).paddingOnly(left: 24, right: 24), + Container( + margin: EdgeInsets.only(left: 24, right: 24, top: 16), + padding: EdgeInsets.only(top: 4, bottom: 4), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: Column( + children: [ + actionItem(AppAssets.call_fill, "Contact Us".needTranslation, () {}, trailingLabel: "9200666666"), + 1.divider, + actionItem(AppAssets.permission, "Permissions".needTranslation, () {}, trailingLabel: "Location, Camera"), + 1.divider, + actionItem(AppAssets.rate, "Rate Our App".needTranslation, () {}, isExternalLink: true), + 1.divider, + actionItem(AppAssets.privacy_terms, "Privacy Policy".needTranslation, () {}, isExternalLink: true), + 1.divider, + actionItem(AppAssets.privacy_terms, "Terms & Conditions".needTranslation, () {}, isExternalLink: true), + ], + ), + ), + CustomButton(icon: AppAssets.minus, text: "Deactivate account".needTranslation, onPressed: () {}).paddingAll(24), ], ); }, @@ -101,6 +196,31 @@ class _ProfileSettingsState extends State { ), ); } + + Widget actionItem(String icon, String label, VoidCallback onPress, {String trailingLabel = "", bool? switchValue, bool isExternalLink = false}) { + return SizedBox( + height: 56, + child: Row( + spacing: 8.h, + children: [ + Utils.buildSvgWithAssets(icon: icon, iconColor: AppColors.greyTextColor), + label.toText14(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + if (trailingLabel.isNotEmpty) trailingLabel.toText14(color: AppColors.greyTextColor, weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1), + switchValue != null + ? Switch( + value: switchValue, + onChanged: (value) {}, + activeColor: AppColors.successColor, + activeTrackColor: AppColors.successColor.withValues(alpha: .15), + ) + : Transform.scale( + scaleX: Utils.appState.isArabic() ? -1 : 1, + child: Utils.buildSvgWithAssets(icon: isExternalLink ? AppAssets.externalLink : AppAssets.arrow_forward), + ) + ], + ).paddingOnly(left: 16, right: 16).onPress(onPress), + ); + } } class FamilyCardWidget extends StatelessWidget { @@ -160,12 +280,8 @@ class FamilyCardWidget extends StatelessWidget { ), ], ).paddingOnly(top: 16, right: 16, left: 16, bottom: 12).expanded, - Divider( - height: 1, - thickness: 1, - color: Color(0x30D2D2D2), - ), - CustomButton(icon: AppAssets.add_family, text: "Add a new family member", onPressed: () {}).paddingOnly(top: 12, right: 16, left: 16, bottom: 16), + 1.divider, + CustomButton(icon: AppAssets.add_family, text: "Add a new family member".needTranslation, onPressed: () {}).paddingOnly(top: 12, right: 16, left: 16, bottom: 16), ], ), ); diff --git a/lib/widgets/app_language_change.dart b/lib/widgets/app_language_change.dart new file mode 100644 index 0000000..de9cda5 --- /dev/null +++ b/lib/widgets/app_language_change.dart @@ -0,0 +1,87 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.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/generated/locale_keys.g.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; + +class AppLanguageChange extends StatefulWidget { + AppLanguageChange({Key? key}) : super(key: key); + + @override + _AppLanguageChangeState createState() { + return _AppLanguageChangeState(); + } +} + +class _AppLanguageChangeState extends State { + String? selectedValue; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) { + selectedValue ??= context.locale.languageCode; + return Column( + spacing: 24.h, + children: [ + Container( + padding: EdgeInsets.only(top: 4, bottom: 4), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true), + child: Column( + children: [ + languageItem("English".needTranslation, "en"), + 1.divider, + languageItem("العربية".needTranslation, "ar"), + ], + ), + ), + CustomButton( + text: LocaleKeys.save.tr(), + onPressed: () { + context.setLocale(selectedValue == 'en' ? Locale('en', 'US') : Locale('ar', 'SA')).then((val) { + Navigator.pop(context); + }); + }), + ], + ); + } + + Widget languageItem(String title, String _value) { + return SizedBox( + height: 72, + child: Row( + spacing: 8.h, + children: [ + Radio( + value: _value, + groupValue: selectedValue, + activeColor: AppColors.errorColor, + onChanged: (value) { + setState(() { + selectedValue = _value; + }); + }, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + title.toText16(weight: FontWeight.w500, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded, + ], + ).paddingOnly(left: 16, right: 16).onPress(() { + setState(() { + selectedValue = _value; + }); + }), + ); + } +} diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart index e1448dc..30e4e5d 100644 --- a/lib/widgets/buttons/custom_button.dart +++ b/lib/widgets/buttons/custom_button.dart @@ -48,6 +48,7 @@ class CustomButton extends StatelessWidget { onTap: isDisabled ? null : onPressed, child: Container( height: height, + padding: padding, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: isDisabled ? Colors.transparent : backgroundColor, borderRadius: borderRadius, @@ -55,28 +56,25 @@ class CustomButton extends StatelessWidget { width: borderWidth, color: isDisabled ? borderColor.withOpacity(0.5) : borderColor, )), - child: Padding( - padding: padding, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (icon != null) - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize), - ), - Text( - text, - style: context.dynamicTextStyle( - fontSize: fontSize.fSize, - color: isDisabled ? textColor.withOpacity(0.5) : textColor, - letterSpacing: -0.4, - fontWeight: fontWeight, - ), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (icon != null) + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize), ), - ], - ), + Text( + text, + style: context.dynamicTextStyle( + fontSize: fontSize.fSize, + color: isDisabled ? textColor.withOpacity(0.5) : textColor, + letterSpacing: -0.4, + fontWeight: fontWeight, + ), + ), + ], ), ) diff --git a/lib/widgets/routes/custom_page_route.dart b/lib/widgets/routes/custom_page_route.dart new file mode 100644 index 0000000..733993f --- /dev/null +++ b/lib/widgets/routes/custom_page_route.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/physics.dart'; + +/// Reusable spring route +class CustomPageRoute extends PageRouteBuilder { + final Widget page; + final AxisDirection direction; + + CustomPageRoute({required this.page, this.direction = AxisDirection.right}) + : super( + transitionDuration: const Duration(milliseconds: 1500), + reverseTransitionDuration: const Duration(milliseconds: 500), + pageBuilder: (_, __, ___) => page, + transitionsBuilder: (context, animation, secondaryAnimation, child) { + final spring = SpringDescription(mass: 1, stiffness: 100, damping: 15); + + // Drive animation with spring + final curvedAnimation = animation.drive( + Tween(begin: 0.0, end: 1.0).chain(CurveTween(curve: _SpringCurve(spring))), + ); + + // Choose offset based on direction + Offset beginOffset; + switch (direction) { + case AxisDirection.left: + beginOffset = const Offset(-1.0, 0.0); + break; + case AxisDirection.right: + beginOffset = const Offset(1.0, 0.0); + break; + case AxisDirection.up: + beginOffset = const Offset(0.0, -1.0); + break; + case AxisDirection.down: + beginOffset = const Offset(0.0, 1.0); + break; + } + + final offsetAnimation = Tween(begin: beginOffset, end: Offset.zero).animate(curvedAnimation); + + return SlideTransition( + position: offsetAnimation, + child: child, + ); + }, + ); +} + +/// Custom spring curve +class _SpringCurve extends Curve { + final SpringDescription spring; + + _SpringCurve(this.spring); + + @override + double transform(double t) { + final sim = SpringSimulation(spring, 0, 1, 0); + return sim.x(t * 1.5); // scaled time + } +} diff --git a/lib/widgets/routes/spring_page_route_builder.dart b/lib/widgets/routes/spring_page_route_builder.dart new file mode 100644 index 0000000..0533408 --- /dev/null +++ b/lib/widgets/routes/spring_page_route_builder.dart @@ -0,0 +1,39 @@ +import 'package:flutter/physics.dart'; +import 'package:flutter/widgets.dart'; + +PageRouteBuilder springPageRoute(Widget page) { + return PageRouteBuilder( + pageBuilder: (_, __, ___) => page, + transitionDuration: const Duration(milliseconds: 1500), + reverseTransitionDuration: const Duration(milliseconds: 500), + transitionsBuilder: (context, animation, secondaryAnimation, child) { + final spring = SpringDescription(mass: 1, stiffness: 100, damping: 15); + + final curvedAnimation = animation.drive( + Tween(begin: 0.0, end: 1.0).chain(CurveTween(curve: _SpringCurve(spring))), + ); + + final offsetAnimation = Tween( + begin: const Offset(-1.0, 0.0), // slide from left + end: Offset.zero, + ).animate(curvedAnimation); + + return SlideTransition( + position: offsetAnimation, + child: child, + ); + }, + ); +} + +class _SpringCurve extends Curve { + final SpringDescription spring; + + _SpringCurve(this.spring); + + @override + double transform(double t) { + final sim = SpringSimulation(spring, 0, 1, 0); + return sim.x(t * 1.5); // scale time so it completes properly + } +}