From 8f0e910e4fb90bb3ecf359214fe79d193adc1916 Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Wed, 17 Sep 2025 15:49:28 +0300 Subject: [PATCH] profile and settings ui completed. --- assets/images/svg/accessibility.svg | 4 + assets/images/svg/arrow_forward.svg | 4 + assets/images/svg/call_fill.svg | 3 + assets/images/svg/email_transparent.svg | 3 + assets/images/svg/emergency.svg | 3 + assets/images/svg/external_link.svg | 4 + assets/images/svg/language_change.svg | 11 ++ assets/images/svg/minus.svg | 4 + assets/images/svg/my_address.svg | 4 + assets/images/svg/permission.svg | 3 + assets/images/svg/privacy_terms.svg | 4 + assets/images/svg/rate.svg | 3 + assets/images/svg/smart_phone_fill.svg | 3 + assets/images/svg/touch_face_id.svg | 4 + lib/core/app_assets.dart | 16 ++- lib/extensions/int_extensions.dart | 4 +- lib/extensions/string_extensions.dart | 6 +- .../profile_settings/profile_settings.dart | 126 ++++++++++++++++-- lib/widgets/buttons/custom_button.dart | 40 +++--- 19 files changed, 214 insertions(+), 35 deletions(-) create mode 100644 assets/images/svg/accessibility.svg create mode 100644 assets/images/svg/arrow_forward.svg create mode 100644 assets/images/svg/call_fill.svg create mode 100644 assets/images/svg/email_transparent.svg create mode 100644 assets/images/svg/emergency.svg create mode 100644 assets/images/svg/external_link.svg create mode 100644 assets/images/svg/language_change.svg create mode 100644 assets/images/svg/minus.svg create mode 100644 assets/images/svg/my_address.svg create mode 100644 assets/images/svg/permission.svg create mode 100644 assets/images/svg/privacy_terms.svg create mode 100644 assets/images/svg/rate.svg create mode 100644 assets/images/svg/smart_phone_fill.svg create mode 100644 assets/images/svg/touch_face_id.svg 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 7fbd212..fbd4945 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'; @@ -115,6 +117,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'; @@ -125,6 +139,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'; @@ -146,5 +161,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/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 509385c..c4e2849 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -132,10 +132,12 @@ 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, TextOverflow? textOverflow}) => + Text( this, textAlign: isCenter ? TextAlign.center : null, maxLines: maxlines, + overflow: textOverflow, style: TextStyle( color: color ?? AppColors.blackColor, fontSize: 14.fSize, @@ -162,6 +164,7 @@ extension EmailValidator on String { bool isBold = false, bool isCenter = false, int? maxlines, + double? height, TextAlign? textAlign, FontWeight? weight, double? letterSpacing = -0.4, @@ -174,6 +177,7 @@ extension EmailValidator on String { color: color ?? AppColors.blackColor, fontSize: 16.fSize, letterSpacing: letterSpacing, + height: height, fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), decoration: isUnderLine ? TextDecoration.underline : null, ), diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index baccbe2..037bb66 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -3,6 +3,7 @@ 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/profile_settings/profile_settings_view_model.dart'; @@ -70,7 +71,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 +84,106 @@ 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(), + RichText( + text: TextSpan( + children: [ + WidgetSpan( + child: Utils.buildSvgWithAssets(icon: AppAssets.saudi_riyal_icon, width: 14.h, height: 14.h, iconColor: Colors.black.withValues(alpha: 0.31)), + ), + TextSpan( + text: " 247", + style: TextStyle(color: AppColors.blackColor, fontSize: 32.fSize, letterSpacing: -4, fontWeight: FontWeight.w600), + ), + ], + ), + ), + 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: () {}, + ), ], ), ) ], - ) + ), + "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, () {}, trailingLabel: "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 +191,28 @@ 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), + ) + : Utils.buildSvgWithAssets(icon: isExternalLink ? AppAssets.externalLink : AppAssets.arrow_forward), + ], + ).paddingOnly(left: 16, right: 16).onPress(onPress), + ); + } } class FamilyCardWidget extends StatelessWidget { @@ -160,12 +272,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/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, + ), + ), + ], ), )