From 8a94f5b4769cc57ae7702b69d19a0409d6f126c1 Mon Sep 17 00:00:00 2001 From: "Aamir.Muhammad" Date: Wed, 10 Jun 2026 14:37:10 +0300 Subject: [PATCH] Accessibility & Image Widgets Update --- assets/langs/ar-SA.json | 13 +- assets/langs/en-US.json | 13 +- lib/core/utils/utils.dart | 210 ++++---- .../models/laser_body_parts_data.dart | 6 +- .../profile_settings_view_model.dart | 50 ++ lib/generated/locale_keys.g.dart | 10 +- lib/main.dart | 59 ++- .../appointment_details_page.dart | 5 +- .../appointment_payment_page.dart | 8 +- .../appointments/my_doctors_page.dart | 10 +- .../widgets/appointment_card.dart | 10 +- .../widgets/appointment_doctor_card.dart | 5 +- .../doctor_row_appointment_rating.dart | 8 +- .../ask_doctor/doctor_response_page.dart | 5 +- .../widgets/ask_doctor_appointment_card.dart | 5 +- .../widgets/doctor_response_transactions.dart | 25 +- .../book_appointment_page.dart | 15 +- .../book_appointment/doctor_profile_page.dart | 10 +- .../immediate_livecare_payment_details.dart | 4 +- .../immediate_livecare_payment_page.dart | 8 +- .../review_appointment_page.dart | 16 +- .../waiting_appointment_payment_page.dart | 8 +- .../book_appointment/widgets/doctor_card.dart | 24 +- .../child_vaccine/my_child_vaccine_page.dart | 2 +- .../comprehensive_checkup_page.dart | 4 +- .../contact_us/feedback_page.dart | 5 +- lib/presentation/contact_us/find_us_page.dart | 7 +- .../contact_us/live_chat_page.dart | 5 +- .../contact_us/widgets/find_us_item_card.dart | 5 +- .../covid19test/covid_payment_screen.dart | 20 +- .../er_online_checkin_payment_page.dart | 8 +- .../wallet_payment_confirm_page.dart | 6 +- lib/presentation/home/service_info_page.dart | 6 +- .../home/widgets/large_service_card.dart | 10 +- .../home/widgets/welcome_widget.dart | 2 +- .../insurance_approval_details_page.dart | 6 +- .../widgets/insurance_approval_card.dart | 5 +- .../lab/lab_result_item_view.dart | 5 +- .../ai_analysis_widget.dart | 10 +- .../medical_file/medical_file_page.dart | 15 +- .../patient_sickleaves_list_page.dart | 5 +- .../medical_file/vaccine_list_page.dart | 5 +- .../medical_file_appointment_card.dart | 5 +- .../widgets/patient_sick_leave_card.dart | 5 +- .../medical_report/medical_reports_page.dart | 5 +- .../widgets/patient_medical_report_card.dart | 5 +- .../my_invoices/my_invoices_details_page.dart | 5 +- .../widgets/invoice_list_card.dart | 5 +- .../widgets/pharmacy_invoice_card.dart | 5 +- ...and_discounts_full_screen_swiper_page.dart | 19 +- .../offers_and_discounts_detailed_page.dart | 6 +- .../offers_and_discounts_page.dart | 5 +- .../widgets/offers_and_discounts.dart | 16 +- lib/presentation/parking/paking_page.dart | 5 +- ...scription_delivery_order_summary_page.dart | 10 +- .../prescription_detail_page.dart | 5 +- .../prescriptions/prescription_item_view.dart | 5 +- .../prescriptions_list_page.dart | 5 +- .../profile_settings/accessibility_page.dart | 476 ++++++++++++++++++ .../profile_settings/profile_settings.dart | 13 +- .../widgets/profile_picture_widget.dart | 6 +- .../radiology_ai_analysis_widget.dart | 3 +- .../radiology/radiology_orders_page.dart | 5 +- .../rate_appointment_clinic.dart | 34 +- .../rate_appointment/widget/doctor_row.dart | 8 +- .../smartwatches/smartwatch_home_page.dart | 8 +- .../smartwatch_instructions_page.dart | 4 +- .../widgets/health_metric_card.dart | 8 +- .../widgets/interactive_body_widget.dart | 22 +- .../tele_consultation/zoom/call_screen.dart | 4 +- .../ancillary_order_payment_page.dart | 6 +- .../ancillary_procedures_details_page.dart | 4 +- .../widgets/ancillary_orders_list.dart | 5 +- .../vital_sign/vital_sign_page.dart | 5 +- lib/widgets/buttons/default_button.dart | 4 +- lib/widgets/chip/app_custom_chip_widget.dart | 2 +- lib/widgets/image_picker.dart | 2 +- lib/widgets/map/location_map_widget.dart | 22 +- .../full_screen_image_viewer.dart | 6 +- lib/widgets/nfc/nfc_reader_sheet.dart | 11 +- lib/widgets/user_avatar_widget.dart | 13 +- 81 files changed, 1058 insertions(+), 382 deletions(-) create mode 100644 lib/presentation/profile_settings/accessibility_page.dart diff --git a/assets/langs/ar-SA.json b/assets/langs/ar-SA.json index 2fd29149..7129fd0b 100644 --- a/assets/langs/ar-SA.json +++ b/assets/langs/ar-SA.json @@ -683,11 +683,20 @@ "emrgNo": "رقم جهة الاتصال في حالات الطوارئ", "modes": "الأوضاع", "vibration": "اهتزاز عند اللمس", - "blindModes": "أوضاع للمكفوفين جزئيًا", + "blindModes": "وضع للمكفوفين جزئياً", "invertTheme": "عكس", "offTheme": "إيقاف", "dimTheme": "خافت", "bwTheme": "أسود وأبيض", + "highContrastTheme": "تباين عالي", + "fontSize": "حجم الخط", + "fontSizeSmall": "صغير", + "fontSizeMedium": "متوسط", + "fontSizeLarge": "كبير", + "saveChanges": "حفظ التغييرات", + "settingsSavedSuccessfully": "تم حفظ الإعدادات بنجاح", + "cardHeading": "عنوان البطاقة", + "buttonText": "نص الزر", "permissions": "إذن", "cameraPermission": "الكاميرا", "locationPermission": "الموقع", @@ -695,7 +704,7 @@ "outOfStockMsg": "لقد أضفت منتجًا غير متوفر حاليًا، يرجى إزالته!", "noArabicLetters": "لا يُسمح باستخدام الحروف العربية", "noOffersAvailable": "لا توجد عروض متاحة!", - "accessibility": "وضع الوصول", + "accessibility": "إمكانية الوصول", "orderStatus": "حالة الطلب", "findUs": "فروعنا", "liveChat": "محادثة مباشرة", diff --git a/assets/langs/en-US.json b/assets/langs/en-US.json index 43ff1c11..29e5d100 100644 --- a/assets/langs/en-US.json +++ b/assets/langs/en-US.json @@ -673,11 +673,20 @@ "emrgNo": "Emergency Contact Number", "modes": "Modes", "vibration": "Vibration Touch Feedback", - "blindModes": "Modes for Partially Blind", + "blindModes": "Mode for partially blind", "invertTheme": "Invert", "offTheme": "Off", "dimTheme": "Dim", "bwTheme": "Black and White", + "highContrastTheme": "High Contrast", + "fontSize": "Font Size", + "fontSizeSmall": "Small", + "fontSizeMedium": "Medium", + "fontSizeLarge": "Large", + "saveChanges": "Save Changes", + "settingsSavedSuccessfully": "Settings saved successfully", + "cardHeading": "Card Heading", + "buttonText": "Button Text", "permissions": "Permission", "cameraPermission": "Camera", "locationPermission": "Location", @@ -685,7 +694,7 @@ "outOfStockMsg": "You have added product which is out of stock now, Please remove that!", "noArabicLetters": "It Is Not Allow to Use Arabic Letters", "noOffersAvailable": "No Offers Available!", - "accessibility": "Accessibility Mode", + "accessibility": "Accessibility", "orderStatus": "Order Status", "findUs": "Find Us", "liveChat": "Live Chat", diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index 35532d69..9e8a044d 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -26,6 +26,8 @@ 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/dialogs/confirm_dialog.dart'; import 'package:hmg_patient_app_new/widgets/loading_dialog.dart'; +import 'package:hmg_patient_app_new/widgets/accessibility/preserve_image_colors.dart'; +import 'package:hmg_patient_app_new/widgets/accessibility/preserve_svg_colors.dart'; import 'package:lottie/lottie.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -120,10 +122,8 @@ class Utils { // ), // )); return !isAddHours - ? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") - .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) - : DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") - .format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( + ? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal()) + : DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.add( Duration( hours: isAddHours ? 3 : 0, ), @@ -356,12 +356,9 @@ class Utils { crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: isSmallWidget ? 0.h : 48.h), - Lottie.asset(AppAnimations.noData, - repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill), + Lottie.asset(AppAnimations.noData, repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill), SizedBox(height: 16.h), - (noDataText ?? LocaleKeys.noDataAvailable.tr()) - .toText14(isBold: true, color: AppColors.greyTextColor, isCenter: true) - .paddingSymmetrical(64.w, 0.h), + (noDataText ?? LocaleKeys.noDataAvailable.tr()).toText14(isBold: true, color: AppColors.greyTextColor, isCenter: true).paddingSymmetrical(64.w, 0.h), SizedBox(height: 16.h), callToActionButton ], @@ -373,8 +370,7 @@ class Utils { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Lottie.asset(AppAnimations.loadingAnimation, - repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill), + Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill), SizedBox(height: 8.h), (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true, isBold: true), SizedBox(height: 8.h), @@ -400,8 +396,7 @@ class Utils { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Lottie.asset(AppAnimations.errorAnimation, - repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), + Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), SizedBox(height: 8.h), (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor).paddingSymmetrical(24.h, 0.h), SizedBox(height: 8.h), @@ -502,8 +497,7 @@ class Utils { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ - Lottie.asset(AppAnimations.warningAnimation, - repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), + Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), SizedBox(height: 8.h), (loadingText ?? LocaleKeys.loadingText.tr()).toText15(color: AppColors.blackColor, letterSpacing: 0), SizedBox(height: 16.h), @@ -623,10 +617,8 @@ class Utils { } // Replace HTML line breaks with newlines - var withLineBreaks = htmlString - .replaceAll(RegExp(r'', multiLine: true), '\n') - .replaceAll(RegExp(r'<\/p>', multiLine: true), '\n') - .replaceAll(RegExp(r'', multiLine: true), '\n'); + var withLineBreaks = + htmlString.replaceAll(RegExp(r'', multiLine: true), '\n').replaceAll(RegExp(r'<\/p>', multiLine: true), '\n').replaceAll(RegExp(r'', multiLine: true), '\n'); // Remove all other HTML tags var withoutTags = withLineBreaks.replaceAll(RegExp(r'<[^>]*>'), ''); @@ -704,20 +696,7 @@ class Utils { final year = parts[0]; // Map month number to short month name (Hijri months) - const hijriMonthNames = [ - 'Muharram', - 'Safar', - 'Rabi I', - 'Rabi II', - 'Jumada I', - 'Jumada II', - 'Rajab', - 'Sha\'ban', - 'Ramadan', - 'Shawwal', - 'Dhu al-Qi\'dah', - 'Dhu al-Hijjah' - ]; + const hijriMonthNames = ['Muharram', 'Safar', 'Rabi I', 'Rabi II', 'Jumada I', 'Jumada II', 'Rajab', 'Sha\'ban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah']; final monthIndex = int.tryParse(parts[1]) ?? 1; final month = hijriMonthNames[monthIndex - 1]; @@ -774,21 +753,23 @@ class Utils { String? errorAsset, }) { final Color? resolvedColor = iconColor ?? (applyThemeColor && AppColors.isDarkMode ? AppColors.textColor : null); - return SvgPicture.asset( - icon, - colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withOpacity(0.5) : resolvedColor, BlendMode.srcIn) : null, - width: width, - height: height, - fit: fit, - errorBuilder: errorAsset != null - ? (context, error, stackTrace) => SvgPicture.asset( - errorAsset, - colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withOpacity(0.5) : resolvedColor, BlendMode.srcIn) : null, - width: width, - height: height, - fit: fit, - ) - : null, + return PreserveSvgColors( + child: SvgPicture.asset( + icon, + colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withValues(alpha: 0.5) : resolvedColor, BlendMode.srcIn) : null, + width: width, + height: height, + fit: fit, + errorBuilder: errorAsset != null + ? (context, error, stackTrace) => SvgPicture.asset( + errorAsset, + colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withValues(alpha: 0.5) : resolvedColor, BlendMode.srcIn) : null, + width: width, + height: height, + fit: fit, + ) + : null, + ), ); } @@ -802,14 +783,16 @@ class Utils { }) { final iconH = height ?? 24.h; final iconW = width ?? 24.w; - return SvgPicture.network( - url, - colorFilter: ColorFilter.mode( - isDisabled ? iconColor.withOpacity(0.5) : iconColor, - BlendMode.srcIn, + return PreserveSvgColors( + child: SvgPicture.network( + url, + colorFilter: ColorFilter.mode( + isDisabled ? iconColor.withValues(alpha: 0.5) : iconColor, + BlendMode.srcIn, + ), + width: iconW, + height: iconH, ), - width: iconW, - height: iconH, ); } @@ -825,14 +808,15 @@ class Utils { }) { final iconH = height ?? 24.h; final iconW = width ?? 24.w; - return Container( - decoration: BoxDecoration( - border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, - borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 12.r) : null, + return PreserveImageColors( + child: Container( + decoration: BoxDecoration( + border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, + borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 12.r) : null, + ), + child: Image.asset(icon, width: iconW, height: iconH, fit: fit), ), - child: Image.asset(icon, width: iconW, height: iconH, fit: fit), ); - // return Image.asset(icon, width: width, height: height, fit: fit, ); } static Widget buildImgWithAssetsWithBorderRadius({ @@ -847,21 +831,23 @@ class Utils { }) { final iconH = height ?? 24.h; final iconW = width ?? 24.w; - return Container( - width: iconW, - height: iconH, - decoration: BoxDecoration( - border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, - borderRadius: borderRadius != null ? BorderRadius.circular(borderRadius ?? 12.r) : null, - image: DecorationImage( - image: AssetImage( - icon, - ), - fit: fit)), + return PreserveImageColors( + child: Container( + width: iconW, + height: iconH, + decoration: BoxDecoration( + border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, + borderRadius: BorderRadius.circular(borderRadius ?? 12.r), + image: DecorationImage( + image: AssetImage( + icon, + ), + fit: fit)), + ), ); } - /// Widget to build an SVG from network + /// Widget to build an image from network - automatically preserves colors in visual modes static Widget buildImgWithNetwork({ required String url, required Color iconColor, @@ -873,16 +859,18 @@ class Utils { }) { final iconH = height ?? 24.h; final iconW = width ?? 24.w; - return Image.network( - url, - width: iconW, - height: iconH, - fit: fit, - errorBuilder: errorBuilder ?? - (_, __, ___) { - //todo_section change the error builder icon that it is returning - return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon); - }, + return PreserveImageColors( + child: Image.network( + url, + width: iconW, + height: iconH, + fit: fit, + errorBuilder: errorBuilder ?? + (_, __, ___) { + //todo_section change the error builder icon that it is returning + return Utils.buildSvgWithAssets(width: iconW, height: iconH, icon: AppAssets.no_visit_icon); + }, + ), ); } @@ -891,26 +879,37 @@ class Utils { spacing: spacing.w, mainAxisSize: MainAxisSize.max, children: [ - Image.asset(AppAssets.mada, width: width.h, height: height.h), - Image.asset( - AppAssets.tamaraEng, - width: width.h, - height: height.h, - fit: BoxFit.contain, - errorBuilder: (context, error, stackTrace) { - debugPrint('Failed to load Tamara PNG in payment methods: $error'); - return Utils.buildSvgWithAssets(icon: AppAssets.tamara, width: 35.h, height: 35.h, fit: BoxFit.contain); - }, + PreserveImageColors( + child: Image.asset(AppAssets.mada, width: width.h, height: height.h), + ), + PreserveImageColors( + child: Image.asset( + AppAssets.tamaraEng, + width: width.h, + height: height.h, + fit: BoxFit.contain, + errorBuilder: (context, error, stackTrace) { + debugPrint('Failed to load Tamara PNG in payment methods: $error'); + return Utils.buildSvgWithAssets(icon: AppAssets.tamara, width: 35.h, height: 35.h, fit: BoxFit.contain); + }, + ), + ), + PreserveImageColors( + child: Image.asset(AppAssets.visa, width: width.h, height: height.h), ), - Image.asset(AppAssets.visa, width: width.h, height: height.h), - Image.asset(AppAssets.mastercard, width: width.h, height: height.h), - Platform.isIOS ? Image.asset(AppAssets.applePay, width: width.h, height: height.h) : SizedBox.shrink(), + PreserveImageColors( + child: Image.asset(AppAssets.mastercard, width: width.h, height: height.h), + ), + Platform.isIOS + ? PreserveImageColors( + child: Image.asset(AppAssets.applePay, width: width.h, height: height.h), + ) + : SizedBox.shrink(), ], ); } - static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, - {bool isSaudiCurrency = true, bool isExpanded = true}) { + static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) { return Row( mainAxisAlignment: isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start, children: [ @@ -965,13 +964,7 @@ class Utils { // text: NumberFormat.currency(locale: 'en_US', symbol: " ", decimalDigits: 0).format(habibWalletAmount), // text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount)).toStringAsFixed(2)}", text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount))}", - style: TextStyle( - color: resolvedTextColor, - fontSize: fontSize ?? 32.f, - letterSpacing: letterSpacing ?? -4, - fontFamily: "Poppins", - fontWeight: fontWeight ?? FontWeight.w600, - height: 1), + style: TextStyle(color: resolvedTextColor, fontSize: fontSize ?? 32.f, letterSpacing: letterSpacing ?? -4, fontFamily: "Poppins", fontWeight: fontWeight ?? FontWeight.w600, height: 1), ), ], ), @@ -1035,12 +1028,7 @@ class Utils { static PatientDoctorAppointmentList? convertToPatientDoctorAppointmentList(HospitalsModel? hospital) { if (hospital == null) return null; return PatientDoctorAppointmentList( - filterName: hospital.name, - distanceInKMs: hospital.distanceInKilometers?.toString(), - projectTopName: hospital.name, - projectBottomName: hospital.name, - model: hospital, - isHMC: hospital.isHMC); + filterName: hospital.name, distanceInKMs: hospital.distanceInKilometers?.toString(), projectTopName: hospital.name, projectBottomName: hospital.name, model: hospital, isHMC: hospital.isHMC); } static HospitalsModel? convertToHospitalsModel(PatientDoctorAppointmentList? item) { diff --git a/lib/features/book_appointments/models/laser_body_parts_data.dart b/lib/features/book_appointments/models/laser_body_parts_data.dart index 2f8b32dc..969aa1d0 100644 --- a/lib/features/book_appointments/models/laser_body_parts_data.dart +++ b/lib/features/book_appointments/models/laser_body_parts_data.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; class LaserBodyParts { static final LaserBodyParts _instance = LaserBodyParts._internal(); @@ -9,11 +9,11 @@ class LaserBodyParts { factory LaserBodyParts() => _instance; static Widget image(String assetUri) { - return new SvgPicture.asset(assetUri, fit: BoxFit.cover, width: 88, height: 88); + return Utils.buildSvgWithAssets(icon: assetUri, fit: BoxFit.cover, width: 88, height: 88, applyThemeColor: false); } static Widget imagePng(String assetUri) { - return Image.asset(assetUri, fit: BoxFit.cover, width: 88, height: 88); + return Utils.buildImgWithAssets(icon: assetUri, fit: BoxFit.cover, width: 88, height: 88); } Map maleBodyMap = { diff --git a/lib/features/profile_settings/profile_settings_view_model.dart b/lib/features/profile_settings/profile_settings_view_model.dart index 02cbc10f..aebdb849 100644 --- a/lib/features/profile_settings/profile_settings_view_model.dart +++ b/lib/features/profile_settings/profile_settings_view_model.dart @@ -16,6 +16,8 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; class ProfileSettingsViewModel extends ChangeNotifier { static const String _darkModeKey = 'is_dark_mode'; + static const String _fontSizeStepKey = 'accessibility_font_size_step'; + static const String _visualModeKey = 'accessibility_visual_mode'; final CacheService _cacheService; final ProfileSettingsRepo profileSettingsRepo; @@ -25,6 +27,13 @@ class ProfileSettingsViewModel extends ChangeNotifier { bool _isDarkMode = false; bool get isDarkMode => _isDarkMode; + // ── Accessibility settings ─────────────────────────────────────────── + int _fontSizeStep = 2; // 0: Small, 1: Small-Medium, 2: Medium (default), 3: Medium-Large, 4: Large + int get fontSizeStep => _fontSizeStep; + + String _visualMode = 'off'; // off, invert, dim, highContrast + String get visualMode => _visualMode; + // ── Update email state ─────────────────────────────────────────────── bool isUpdateEmailLoading = false; bool isUpdateEmailSuccess = false; @@ -163,6 +172,47 @@ class ProfileSettingsViewModel extends ChangeNotifier { notifyListeners(); } + // ── Accessibility settings ─────────────────────────────────────────── + + /// Load accessibility settings at app startup + void loadAccessibilitySettings() { + _fontSizeStep = _cacheService.getInt(key: _fontSizeStepKey) ?? 2; + _visualMode = _cacheService.getString(key: _visualModeKey) ?? 'off'; + // No notifyListeners() here — we are called before build. + } + + /// Update font size step and save to cache + void setFontSizeStep(int step) { + _fontSizeStep = step; + _cacheService.saveInt(key: _fontSizeStepKey, value: step); + notifyListeners(); + } + + /// Update visual mode and save to cache + void setVisualMode(String mode) { + _visualMode = mode; + _cacheService.saveString(key: _visualModeKey, value: mode); + notifyListeners(); + } + + /// Get font scale multiplier based on current font size step + double getFontScaleMultiplier() { + switch (_fontSizeStep) { + case 0: + return 0.85; // Small + case 1: + return 0.95; // Small-Medium + case 2: + return 1.0; // Medium (default) + case 3: + return 1.15; // Medium-Large + case 4: + return 1.3; // Large + default: + return 1.0; + } + } + // ── Update patient info ────────────────────────────────────────────── Future updatePatientInfo({ diff --git a/lib/generated/locale_keys.g.dart b/lib/generated/locale_keys.g.dart index 51e27211..08a3b905 100644 --- a/lib/generated/locale_keys.g.dart +++ b/lib/generated/locale_keys.g.dart @@ -687,6 +687,15 @@ abstract class LocaleKeys { static const offTheme = 'offTheme'; static const dimTheme = 'dimTheme'; static const bwTheme = 'bwTheme'; + static const highContrastTheme = 'highContrastTheme'; + static const fontSize = 'fontSize'; + static const fontSizeSmall = 'fontSizeSmall'; + static const fontSizeMedium = 'fontSizeMedium'; + static const fontSizeLarge = 'fontSizeLarge'; + static const saveChanges = 'saveChanges'; + static const settingsSavedSuccessfully = 'settingsSavedSuccessfully'; + static const cardHeading = 'cardHeading'; + static const buttonText = 'buttonText'; static const permissions = 'permissions'; static const cameraPermission = 'cameraPermission'; static const locationPermission = 'locationPermission'; @@ -1578,7 +1587,6 @@ abstract class LocaleKeys { static const waterConsumption = 'waterConsumption'; static const selectNumberOfReminders = 'selectNumberOfReminders'; static const h2oSettings = 'h2oSettings'; - static const settingsSavedSuccessfully = 'settingsSavedSuccessfully'; static const yourName = 'yourName'; static const ageYears = 'ageYears'; static const numberOfRemindersInADay = 'numberOfRemindersInADay'; diff --git a/lib/main.dart b/lib/main.dart index 0119cd1c..6e3ef8cc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -134,6 +134,9 @@ Future callInitializations() async { // Restore persisted dark-mode preference before the first frame. getIt.get().loadDarkMode(); + + // Restore persisted accessibility settings before the first frame. + getIt.get().loadAccessibilitySettings(); } void main() async { @@ -282,6 +285,55 @@ void main() async { class MyApp extends StatelessWidget { const MyApp({super.key}); + /// Apply visual mode filter to the entire app + Widget _applyVisualMode(String visualMode, Widget child) { + switch (visualMode) { + case 'invert': + // Invert colors app-wide (red becomes cyan for colorblind users) + return ColorFiltered( + colorFilter: const ColorFilter.matrix([ + -1, 0, 0, 0, 255, // Red channel inverted + 0, -1, 0, 0, 255, // Green channel inverted + 0, 0, -1, 0, 255, // Blue channel inverted + 0, 0, 0, 1, 0, // Alpha channel unchanged + ]), + child: child, + ); + + case 'dim': + // Apply dim effect with opacity overlay + return Stack( + children: [ + child, + Positioned.fill( + child: IgnorePointer( + child: Container( + color: Colors.black.withValues(alpha: 0.3), // 30% black overlay for dimming + ), + ), + ), + ], + ); + + case 'highContrast': + // Apply desaturation for high contrast (removes colors, leaves black and white) + return ColorFiltered( + colorFilter: const ColorFilter.matrix([ + 0.2126, 0.7152, 0.0722, 0, 0, // Red channel (grayscale) + 0.2126, 0.7152, 0.0722, 0, 0, // Green channel (grayscale) + 0.2126, 0.7152, 0.0722, 0, 0, // Blue channel (grayscale) + 0, 0, 0, 1, 0, // Alpha channel unchanged + ]), + child: child, + ); + + case 'off': + default: + // No filter applied + return child; + } + } + @override Widget build(BuildContext context) { return SafeArea( @@ -300,13 +352,16 @@ class MyApp extends StatelessWidget { // title: 'Dr. AlHabib', title: 'Dr. AlHabib Beta', builder: (context, mchild) { - return MediaQuery( + Widget child = MediaQuery( data: MediaQuery.of(context).copyWith( - textScaler: TextScaler.noScaling, + textScaler: TextScaler.linear(profileVm.getFontScaleMultiplier()), alwaysUse24HourFormat: true, ), child: mchild!, ); + + // Apply visual mode filters app-wide + return _applyVisualMode(profileVm.visualMode, child); }, showSemanticsDebugger: false, debugShowCheckedModeBanner: false, diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index ddf9ec86..46fd5209 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -570,8 +570,9 @@ class _AppointmentDetailsPageState extends State { clipBehavior: Clip.hardEdge, borderRadius: BorderRadius.circular(24.r), // Todo: what is this???? Api Key??? 😲 - child: Image.network( - "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", + child: Utils.buildImgWithNetwork( + url: "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=${(MediaQuery.of(context).size.width * 1.5).toInt()}x${(MediaQuery.of(context).size.height * 0.35).toInt()}&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", + iconColor: AppColors.transparent, fit: BoxFit.cover, width: double.infinity, ), diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index bae7514c..f3aff049 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -112,7 +112,7 @@ class _AppointmentPaymentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(AppAssets.mada, width: 72.h, height: 25.h) + Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h) .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), @@ -230,9 +230,9 @@ class _AppointmentPaymentPageState extends State { children: [ Row( children: [ - Image.asset(AppAssets.visa, width: 50.h, height: 50.h), + Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h), SizedBox(width: 8.h), - Image.asset(AppAssets.mastercard, width: 40.h, height: 40.h), + Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), ], ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), @@ -352,7 +352,7 @@ class _AppointmentPaymentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(AppAssets.tamaraEng, width: 72.h, height: 25.h) + Utils.buildImgWithAssets(icon: AppAssets.tamaraEng, width: 72.h, height: 25.h) .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), LocaleKeys.tamara.tr(context: context) diff --git a/lib/presentation/appointments/my_doctors_page.dart b/lib/presentation/appointments/my_doctors_page.dart index 0c574a4c..9ccf471e 100644 --- a/lib/presentation/appointments/my_doctors_page.dart +++ b/lib/presentation/appointments/my_doctors_page.dart @@ -139,8 +139,9 @@ class _MyDoctorsPageState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, @@ -261,8 +262,9 @@ class _MyDoctorsPageState extends State { Row( mainAxisSize: MainAxisSize.min, children: [ - Image.network( - (doctor?.doctorImageURL ?? doctor?.doctorImage ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"), + Utils.buildImgWithNetwork( + url: (doctor?.doctorImageURL ?? doctor?.doctorImage ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"), + iconColor: AppColors.transparent, width: 24.w, height: 24.h, fit: BoxFit.cover, diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 03c12c3b..8e871dcb 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -227,10 +227,11 @@ class _AppointmentCardState extends State { Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - widget.isLoading + Utils.buildImgWithNetwork( + url: widget.isLoading ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' : widget.patientAppointmentHistoryResponseModel.doctorImageURL!, + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, @@ -280,8 +281,9 @@ class _AppointmentCardState extends State { SizedBox(width: 12.w), (widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) - ? Image.network( - widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", + ? Utils.buildImgWithNetwork( + url: widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", + iconColor: AppColors.transparent, width: 20.h, height: 15.h, fit: BoxFit.cover, diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart index 299c3db9..d064b4e8 100644 --- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart +++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart @@ -58,8 +58,9 @@ class AppointmentDoctorCard extends StatelessWidget { children: [ Column( children: [ - Image.network( - patientAppointmentHistoryResponseModel.doctorImageURL!, + Utils.buildImgWithNetwork( + url: patientAppointmentHistoryResponseModel.doctorImageURL!, + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/appointments/widgets/doctor_row_appointment_rating.dart b/lib/presentation/appointments/widgets/doctor_row_appointment_rating.dart index 8872f4ea..df15173c 100644 --- a/lib/presentation/appointments/widgets/doctor_row_appointment_rating.dart +++ b/lib/presentation/appointments/widgets/doctor_row_appointment_rating.dart @@ -10,6 +10,9 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'dart:ui' as ui; +import '../../../core/utils/utils.dart'; +import '../../../theme/colors.dart'; + class BuildDoctorRowAppointmentRating extends StatelessWidget { final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; @@ -22,8 +25,9 @@ class BuildDoctorRowAppointmentRating extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - patientAppointmentHistoryResponseModel.doctorImageURL!, + Utils.buildImgWithNetwork( + url: patientAppointmentHistoryResponseModel.doctorImageURL!, + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/ask_doctor/doctor_response_page.dart b/lib/presentation/ask_doctor/doctor_response_page.dart index 922d2fdb..5591d777 100644 --- a/lib/presentation/ask_doctor/doctor_response_page.dart +++ b/lib/presentation/ask_doctor/doctor_response_page.dart @@ -86,8 +86,9 @@ class _DoctorResponsePageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", + iconColor: AppColors.blackBgColor, width: 50.w, height: 50.h, fit: BoxFit.cover, diff --git a/lib/presentation/ask_doctor/widgets/ask_doctor_appointment_card.dart b/lib/presentation/ask_doctor/widgets/ask_doctor_appointment_card.dart index 4ebd9b74..6eae8870 100644 --- a/lib/presentation/ask_doctor/widgets/ask_doctor_appointment_card.dart +++ b/lib/presentation/ask_doctor/widgets/ask_doctor_appointment_card.dart @@ -87,8 +87,9 @@ class AskDoctorAppointmentCard extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - askDoctorAppointmentHistoryList.doctorImageURL!, + Utils.buildImgWithNetwork( + url: askDoctorAppointmentHistoryList.doctorImageURL!, + iconColor: AppColors.blackBgColor, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart b/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart index 59516d15..1343c36f 100644 --- a/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart +++ b/lib/presentation/ask_doctor/widgets/doctor_response_transactions.dart @@ -28,8 +28,9 @@ class DoctorResponseTransactions extends StatelessWidget { Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", + iconColor: AppColors.blackBgColor, width: 50.w, height: 50.h, fit: BoxFit.cover, @@ -85,7 +86,11 @@ class DoctorResponseTransactions extends StatelessWidget { SizedBox(height: 16.h), Row( children: [ - Image.asset(getIt.get().getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, width: 42.w, height: 42.h), + Utils.buildImgWithAssets( + icon: getIt.get().getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, + width: 42.w, + height: 42.h, + ), SizedBox(width: 8.w), Container( padding: EdgeInsets.all(8.w), @@ -116,10 +121,16 @@ class DoctorResponseTransactions extends StatelessWidget { borderRadius: BorderRadius.circular(8.r), ), child: doctorResponseModel.transactions![index].doctorResponse!.toText12(color: AppColors.textColor, isBold: true), - ), - SizedBox(width: 8.w), - Image.network("https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", fit: BoxFit.cover, width: 42.w, height: 42.h).circle(100.r), - ], + ), + SizedBox(width: 8.w), + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", + iconColor: AppColors.blackBgColor, + fit: BoxFit.cover, + width: 42.w, + height: 42.h, + ).circle(100.r), + ], ), ], ); diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart index 00a0e5a2..6bbac9ec 100644 --- a/lib/presentation/book_appointment/book_appointment_page.dart +++ b/lib/presentation/book_appointment/book_appointment_page.dart @@ -132,8 +132,9 @@ class _BookAppointmentPageState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 64.h, height: 64.h, fit: BoxFit.cover, @@ -191,8 +192,9 @@ class _BookAppointmentPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, + Utils.buildImgWithNetwork( + url: myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, + iconColor: AppColors.transparent, width: 64.h, height: 64.h, fit: BoxFit.cover, @@ -383,8 +385,9 @@ class _BookAppointmentPageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - myAppointmentsVM.patientFavouriteDoctorsList[index].doctorImageUrl!, + Utils.buildImgWithNetwork( + url: myAppointmentsVM.patientFavouriteDoctorsList[index].doctorImageUrl!, + iconColor: AppColors.transparent, width: 64.h, height: 64.h, fit: BoxFit.cover, diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart index 044db504..be097651 100644 --- a/lib/presentation/book_appointment/doctor_profile_page.dart +++ b/lib/presentation/book_appointment/doctor_profile_page.dart @@ -83,8 +83,9 @@ class DoctorProfilePage extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL!, + Utils.buildImgWithNetwork( + url: bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL!, + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, @@ -109,8 +110,9 @@ class DoctorProfilePage extends StatelessWidget { ), ], ), - Image.network( - bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL!, + Utils.buildImgWithNetwork( + url: bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL!, + iconColor: AppColors.transparent, width: 32.h, height: 32.h, fit: BoxFit.cover, diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart index dd8422ee..ed323629 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_details.dart @@ -66,8 +66,8 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget { padding: EdgeInsets.all(16.h), child: Row( children: [ - Image.asset( - appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, + Utils.buildImgWithAssets( + icon: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, width: 52.h, height: 52.h, ), diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart index e2bb0c17..b0f00317 100644 --- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart +++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart @@ -106,7 +106,7 @@ class _ImmediateLiveCarePaymentPageState extends State { children: [ Row( children: [ - Image.network( - bookAppointmentsViewModel.selectedDoctor.doctorImageURL ?? + Utils.buildImgWithNetwork( + url: bookAppointmentsViewModel.selectedDoctor.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", + iconColor: AppColors.transparent, width: 50.h, height: 50.h, fit: BoxFit.cover, @@ -104,9 +105,10 @@ class _ReviewAppointmentPageState extends State { SizedBox(width: 12.w), (bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL != null && bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!.isNotEmpty) - ? Image.network( - bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ?? + ? Utils.buildImgWithNetwork( + url: bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", + iconColor: AppColors.transparent, width: 20.h, height: 15.h, fit: BoxFit.cover, @@ -334,12 +336,10 @@ class _ReviewAppointmentPageState extends State { // Show default image (no image data or user has no uploaded image) print('📷 Showing default avatar for user $currentPatientId'); - return Image.asset( - defaultImage, - key: ValueKey('default_$currentPatientId'), + return Utils.buildImgWithAssets( + icon: defaultImage, width: 52.w, height: 52.h, - gaplessPlayback: true, ); } diff --git a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart index 95fafe9c..c40c712c 100644 --- a/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart +++ b/lib/presentation/book_appointment/waiting_appointment/waiting_appointment_payment_page.dart @@ -116,7 +116,7 @@ class _WaitingAppointmentPaymentPageState extends State { ), child: Row( children: [ - Image.asset(children[index]['image'], width: 24.h, height: 24.h), + Utils.buildImgWithAssets(icon: children[index]['image'], width: 24.h, height: 24.h), SizedBox(width: 8.w), Text( "${children[index]['name']} (${children[index]['gender']})", diff --git a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart index 06056764..7e7bc613 100644 --- a/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart +++ b/lib/presentation/comprehensive_checkup/comprehensive_checkup_page.dart @@ -282,8 +282,8 @@ class _ComprehensiveCheckupPageState extends State { final String imagePath = appStateLocal.isArabic() ? AppAssets.comprehensiveCheckupAr : AppAssets.comprehensiveCheckupEn; return Stack( children: [ - Image.asset( - imagePath, + Utils.buildImgWithAssets( + icon: imagePath, width: double.infinity, fit: BoxFit.cover, ).paddingAll(16.w), diff --git a/lib/presentation/contact_us/feedback_page.dart b/lib/presentation/contact_us/feedback_page.dart index 00576e9d..32359fef 100644 --- a/lib/presentation/contact_us/feedback_page.dart +++ b/lib/presentation/contact_us/feedback_page.dart @@ -259,8 +259,9 @@ class FeedbackPage extends StatelessWidget { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - contactUsViewModel.patientFeedbackSelectedAppointment!.doctorImageURL!, + Utils.buildImgWithNetwork( + url: contactUsViewModel.patientFeedbackSelectedAppointment!.doctorImageURL!, + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/contact_us/find_us_page.dart b/lib/presentation/contact_us/find_us_page.dart index 4a68b2aa..379540a3 100644 --- a/lib/presentation/contact_us/find_us_page.dart +++ b/lib/presentation/contact_us/find_us_page.dart @@ -16,6 +16,8 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart'; import 'package:provider/provider.dart'; +import '../../core/utils/utils.dart'; + class FindUsPage extends StatelessWidget { FindUsPage({super.key}); @@ -120,8 +122,9 @@ class FindUsPage extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/contact_us/live_chat_page.dart b/lib/presentation/contact_us/live_chat_page.dart index 107f5ba4..718294b4 100644 --- a/lib/presentation/contact_us/live_chat_page.dart +++ b/lib/presentation/contact_us/live_chat_page.dart @@ -70,8 +70,9 @@ class LiveChatPage extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/contact_us/widgets/find_us_item_card.dart b/lib/presentation/contact_us/widgets/find_us_item_card.dart index 1f7f2443..6db34a4d 100644 --- a/lib/presentation/contact_us/widgets/find_us_item_card.dart +++ b/lib/presentation/contact_us/widgets/find_us_item_card.dart @@ -78,8 +78,9 @@ class FindUsItemCard extends StatelessWidget { : SizedBox.shrink(), Row( children: [ - Image.network( - getHMGLocationsModel.projectImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + Utils.buildImgWithNetwork( + url: getHMGLocationsModel.projectImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 40.h, height: 40.h, fit: BoxFit.cover, diff --git a/lib/presentation/covid19test/covid_payment_screen.dart b/lib/presentation/covid19test/covid_payment_screen.dart index a846828f..db7268c5 100644 --- a/lib/presentation/covid19test/covid_payment_screen.dart +++ b/lib/presentation/covid19test/covid_payment_screen.dart @@ -211,7 +211,7 @@ class _CovidPaymentScreenState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(AppAssets.mada, width: 72.h, height: 25.h), + Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h), SizedBox(height: 16.h), LocaleKeys.mada.tr(context: context).toText16(isBold: true), ], @@ -252,9 +252,9 @@ class _CovidPaymentScreenState extends State { children: [ Row( children: [ - Image.asset(AppAssets.visa, width: 50.h, height: 50.h), + Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h), SizedBox(width: 8.h), - Image.asset(AppAssets.mastercard, width: 40.h, height: 40.h), + Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), ], ), SizedBox(height: 16.h), @@ -296,21 +296,11 @@ class _CovidPaymentScreenState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset( - AppAssets.tamaraEng, + Utils.buildImgWithAssets( + icon: AppAssets.tamaraEng, width: 72.h, height: 25.h, fit: BoxFit.contain, - // If PNG fails to load for any reason, log and fallback to SVG asset - errorBuilder: (context, error, stackTrace) { - debugPrint('Failed to load Tamara PNG asset: $error'); - return Utils.buildSvgWithAssets( - icon: AppAssets.tamara, - width: 72.h, - height: 25.h, - fit: BoxFit.contain, - ); - }, ), SizedBox(height: 16.h), LocaleKeys.tamara.tr(context: context).toText16(isBold: true), diff --git a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_page.dart b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_page.dart index fca64321..64856226 100644 --- a/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_page.dart +++ b/lib/presentation/emergency_services/er_online_checkin/er_online_checkin_payment_page.dart @@ -102,7 +102,7 @@ class _ErOnlineCheckinPaymentPageState extends State Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(AppAssets.mada, width: 72.h, height: 25.h), + Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h), SizedBox(height: 16.h), LocaleKeys.mada.tr(context: context).toText16(isBold: true), ], @@ -188,9 +188,9 @@ class _ErOnlineCheckinPaymentPageState extends State children: [ Row( children: [ - Image.asset(AppAssets.visa, width: 50.h, height: 50.h), + Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h), SizedBox(width: 8.h), - Image.asset(AppAssets.mastercard, width: 40.h, height: 40.h), + Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), ], ), SizedBox(height: 16.h), @@ -277,7 +277,7 @@ class _ErOnlineCheckinPaymentPageState extends State Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(AppAssets.tamaraEng, width: 72.h, height: 25.h), + Utils.buildImgWithAssets(icon: AppAssets.tamaraEng, width: 72.h, height: 25.h), SizedBox(height: 16.h), LocaleKeys.tamara.tr(context: context).toText16(isBold: true), ], diff --git a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart index dd3c5ff8..e52e3548 100644 --- a/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart +++ b/lib/presentation/habib_wallet/wallet_payment_confirm_page.dart @@ -85,7 +85,7 @@ class _WalletPaymentConfirmPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: false), + Utils.buildImgWithAssets(icon: AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: false), SizedBox(height: 16.h), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: false), ], @@ -183,9 +183,9 @@ class _WalletPaymentConfirmPageState extends State { children: [ Row( children: [ - Image.asset(AppAssets.visa, width: 40.h, height: 40.h), + Utils.buildImgWithAssets(icon: AppAssets.visa, width: 40.h, height: 40.h), SizedBox(width: 8.h), - Image.asset(AppAssets.mastercard, width: 40.h, height: 40.h), + Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), ], ).toShimmer2(isShow: false), SizedBox(height: 16.h), diff --git a/lib/presentation/home/service_info_page.dart b/lib/presentation/home/service_info_page.dart index 75df9437..00f264b5 100644 --- a/lib/presentation/home/service_info_page.dart +++ b/lib/presentation/home/service_info_page.dart @@ -13,6 +13,8 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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 '../../core/utils/utils.dart'; + class ServiceInfoPage extends StatelessWidget { final String serviceName; final String serviceHeader; @@ -37,8 +39,8 @@ class ServiceInfoPage extends StatelessWidget { borderRadius: BorderRadius.circular(24.r), child: Transform.flip( flipX: getIt.get().isArabic(), - child: Image.asset( - serviceImage, + child: Utils.buildImgWithAssets( + icon: serviceImage, fit: BoxFit.fitHeight, // height: 480.h, // width: 520.w, diff --git a/lib/presentation/home/widgets/large_service_card.dart b/lib/presentation/home/widgets/large_service_card.dart index 72b11ef3..7c037604 100644 --- a/lib/presentation/home/widgets/large_service_card.dart +++ b/lib/presentation/home/widgets/large_service_card.dart @@ -56,8 +56,8 @@ class LargeServiceCard extends StatelessWidget { topLeft: Radius.circular(24.r), topRight: Radius.circular(24.r), ), - child: Image.asset( - serviceCardData.largeCardIcon, + child: Utils.buildImgWithAssets( + icon: serviceCardData.largeCardIcon, fit: BoxFit.cover, width: double.infinity, height: isFoldable ? 190.h : (isTablet ? 200.h : 180.h), @@ -72,7 +72,7 @@ class LargeServiceCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ isPNG - ? Image.asset(serviceCardData.icon, width: 35.h, height: 35.h) + ? Utils.buildImgWithAssets(icon: serviceCardData.icon, width: 35.h, height: 35.h) : Container( height: 48.h, width: 48.h, @@ -204,7 +204,7 @@ class FadedLargeServiceCard extends StatelessWidget { borderRadius: BorderRadius.circular(24.r), child: Transform.flip( flipX: getIt.get().isArabic(), - child: Image.asset(serviceCardData.largeCardIcon, fit: BoxFit.cover, width: 520.w, height: 250.h)), + child: Utils.buildImgWithAssets(icon: serviceCardData.largeCardIcon, fit: BoxFit.cover, width: 520.w, height: 250.h)), ), Positioned( top: 0, @@ -234,7 +234,7 @@ class FadedLargeServiceCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ isPNG - ? Image.asset(serviceCardData.icon, width: 32.h, height: 32.h).circle(100.h) + ? Utils.buildImgWithAssets(icon: serviceCardData.icon, width: 32.h, height: 32.h).circle(100.h) : Container( height: 32.h, width: 32.h, diff --git a/lib/presentation/home/widgets/welcome_widget.dart b/lib/presentation/home/widgets/welcome_widget.dart index bdd71b53..039da8c1 100644 --- a/lib/presentation/home/widgets/welcome_widget.dart +++ b/lib/presentation/home/widgets/welcome_widget.dart @@ -63,7 +63,7 @@ class WelcomeWidget extends StatelessWidget { ), imageWidget ?? (imageUrl != null - ? Image.asset(imageUrl!, width: 40, height: 40) + ? Utils.buildImgWithAssets(icon: imageUrl!, width: 40, height: 40) : SizedBox(width: 40, height: 40)), Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/presentation/insurance/insurance_approval_details_page.dart b/lib/presentation/insurance/insurance_approval_details_page.dart index e3b684bf..b1fce84a 100644 --- a/lib/presentation/insurance/insurance_approval_details_page.dart +++ b/lib/presentation/insurance/insurance_approval_details_page.dart @@ -6,6 +6,7 @@ import 'package:hmg_patient_app_new/core/app_export.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/date_util.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/features/insurance/insurance_view_model.dart'; @@ -82,8 +83,9 @@ class InsuranceApprovalDetailsPage extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - insuranceApprovalResponseModel.doctorImageURL!, + Utils.buildImgWithNetwork( + url: insuranceApprovalResponseModel.doctorImageURL!, + iconColor: AppColors.blackBgColor, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/insurance/widgets/insurance_approval_card.dart b/lib/presentation/insurance/widgets/insurance_approval_card.dart index 8357a2d9..34662ef4 100644 --- a/lib/presentation/insurance/widgets/insurance_approval_card.dart +++ b/lib/presentation/insurance/widgets/insurance_approval_card.dart @@ -106,8 +106,9 @@ class InsuranceApprovalCard extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - isLoading ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png" : insuranceApprovalResponseModel.doctorImageURL!, + Utils.buildImgWithNetwork( + url: isLoading ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png" : insuranceApprovalResponseModel.doctorImageURL!, + iconColor: AppColors.blackBgColor, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/lab/lab_result_item_view.dart b/lib/presentation/lab/lab_result_item_view.dart index 554bd8f2..d03c02df 100644 --- a/lib/presentation/lab/lab_result_item_view.dart +++ b/lib/presentation/lab/lab_result_item_view.dart @@ -55,8 +55,9 @@ class LabResultItemView extends StatelessWidget { SizedBox(height: 8.h), Row( children: [ - Image.network( - isLoading ? "" : labOrder!.doctorImageURL!, + Utils.buildImgWithNetwork( + url: isLoading ? "" : labOrder!.doctorImageURL!, + iconColor: AppColors.transparent, width: 24.h, height: 24.h, fit: BoxFit.cover, diff --git a/lib/presentation/lab/lab_result_via_clinic/ai_analysis_widget.dart b/lib/presentation/lab/lab_result_via_clinic/ai_analysis_widget.dart index ebf802df..b643515f 100644 --- a/lib/presentation/lab/lab_result_via_clinic/ai_analysis_widget.dart +++ b/lib/presentation/lab/lab_result_via_clinic/ai_analysis_widget.dart @@ -1,9 +1,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/dependencies.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/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_order_response_by_ai_model.dart'; @@ -37,7 +37,13 @@ class AiAnalysisWidget extends StatelessWidget { Container( padding: EdgeInsets.all(8.r), decoration: BoxDecoration(gradient: AppColors.aiLinearGradient, borderRadius: BorderRadius.circular(8.r)), - child: SvgPicture.asset(AppAssets.aiOverView, colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), height: 18.r, width: 18.r), + child: Utils.buildSvgWithAssets( + icon: AppAssets.aiOverView, + iconColor: Colors.white, + height: 18.r, + width: 18.r, + applyThemeColor: false, + ), ), ], ), diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart index 6bbd5074..81343e42 100644 --- a/lib/presentation/medical_file/medical_file_page.dart +++ b/lib/presentation/medical_file/medical_file_page.dart @@ -908,8 +908,9 @@ class _MedicalFilePageState extends State { child: FadeInAnimation( child: Row( children: [ - Image.network( - prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!, + Utils.buildImgWithNetwork( + url: prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!, + iconColor: AppColors.transparent, width: 40.h, height: 40.h, fit: BoxFit.cover, @@ -1059,8 +1060,9 @@ class _MedicalFilePageState extends State { ? Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 64.h, height: 64.h, fit: BoxFit.cover, @@ -1106,8 +1108,9 @@ class _MedicalFilePageState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - Image.network( - myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, + Utils.buildImgWithNetwork( + url: myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, + iconColor: AppColors.transparent, width: 64.h, height: 64.h, fit: BoxFit.cover, diff --git a/lib/presentation/medical_file/patient_sickleaves_list_page.dart b/lib/presentation/medical_file/patient_sickleaves_list_page.dart index cfdaaa7c..c31d80ac 100644 --- a/lib/presentation/medical_file/patient_sickleaves_list_page.dart +++ b/lib/presentation/medical_file/patient_sickleaves_list_page.dart @@ -139,8 +139,9 @@ class _PatientSickleavesListPageState extends State { Row( mainAxisSize: MainAxisSize.min, children: [ - Image.network( - model.patientSickLeaveList[index].doctorImageURL!, + Utils.buildImgWithNetwork( + url: model.patientSickLeaveList[index].doctorImageURL!, + iconColor: AppColors.transparent, width: 24.h, height: 24.h, fit: BoxFit.fill, diff --git a/lib/presentation/medical_file/vaccine_list_page.dart b/lib/presentation/medical_file/vaccine_list_page.dart index d6c29676..a58802cc 100644 --- a/lib/presentation/medical_file/vaccine_list_page.dart +++ b/lib/presentation/medical_file/vaccine_list_page.dart @@ -83,8 +83,9 @@ class _VaccineListPageState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart index c0a5492b..5ddf25ea 100644 --- a/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart +++ b/lib/presentation/medical_file/widgets/medical_file_appointment_card.dart @@ -163,9 +163,10 @@ class _MedicalFileAppointmentCardState extends State children: [ Row( children: [ - Image.network( - widget.patientAppointmentHistoryResponseModel.doctorImageURL ?? + Utils.buildImgWithNetwork( + url: widget.patientAppointmentHistoryResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 30.h, height: 30.h, fit: BoxFit.fill, diff --git a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart index 667e1a30..63aac378 100644 --- a/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart +++ b/lib/presentation/medical_file/widgets/patient_sick_leave_card.dart @@ -56,8 +56,9 @@ class PatientSickLeaveCard extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientSickLeavesResponseModel.doctorImageURL!, + Utils.buildImgWithNetwork( + url: isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientSickLeavesResponseModel.doctorImageURL!, + iconColor: AppColors.transparent, width: 30.h, height: 30.h, fit: BoxFit.cover, diff --git a/lib/presentation/medical_report/medical_reports_page.dart b/lib/presentation/medical_report/medical_reports_page.dart index ee287662..e03a53a0 100644 --- a/lib/presentation/medical_report/medical_reports_page.dart +++ b/lib/presentation/medical_report/medical_reports_page.dart @@ -241,8 +241,9 @@ class _MedicalReportsPageState extends State { Row( mainAxisSize: MainAxisSize.min, children: [ - Image.network( - report.doctorImageURL!, + Utils.buildImgWithNetwork( + url: report.doctorImageURL!, + iconColor: AppColors.transparent, width: 24.h, height: 24.h, fit: BoxFit.fill, diff --git a/lib/presentation/medical_report/widgets/patient_medical_report_card.dart b/lib/presentation/medical_report/widgets/patient_medical_report_card.dart index 49cb0e3a..f074022e 100644 --- a/lib/presentation/medical_report/widgets/patient_medical_report_card.dart +++ b/lib/presentation/medical_report/widgets/patient_medical_report_card.dart @@ -42,8 +42,9 @@ class PatientMedicalReportCard extends StatelessWidget { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientMedicalReportResponseModel.doctorImageURL!, + Utils.buildImgWithNetwork( + url: isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientMedicalReportResponseModel.doctorImageURL!, + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.fill, diff --git a/lib/presentation/my_invoices/my_invoices_details_page.dart b/lib/presentation/my_invoices/my_invoices_details_page.dart index 9f0370a0..2b539b24 100644 --- a/lib/presentation/my_invoices/my_invoices_details_page.dart +++ b/lib/presentation/my_invoices/my_invoices_details_page.dart @@ -149,8 +149,9 @@ class _MyInvoicesDetailsPageState extends State { children: [ Column( children: [ - Image.network( - widget.getInvoiceDetailsResponseModel.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', + Utils.buildImgWithNetwork( + url: widget.getInvoiceDetailsResponseModel.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/my_invoices/widgets/invoice_list_card.dart b/lib/presentation/my_invoices/widgets/invoice_list_card.dart index 1e9aefd8..8fea72fc 100644 --- a/lib/presentation/my_invoices/widgets/invoice_list_card.dart +++ b/lib/presentation/my_invoices/widgets/invoice_list_card.dart @@ -102,8 +102,9 @@ class InvoiceListCard extends StatelessWidget { children: [ Column( children: [ - Image.network( - getInvoicesListResponseModel.doctorImageUrl ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', + Utils.buildImgWithNetwork( + url: getInvoicesListResponseModel.doctorImageUrl ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/my_invoices/widgets/pharmacy_invoice_card.dart b/lib/presentation/my_invoices/widgets/pharmacy_invoice_card.dart index 6d672b38..2aaefcac 100644 --- a/lib/presentation/my_invoices/widgets/pharmacy_invoice_card.dart +++ b/lib/presentation/my_invoices/widgets/pharmacy_invoice_card.dart @@ -71,8 +71,9 @@ class PharmacyInvoiceCard extends StatelessWidget { children: [ Column( children: [ - Image.network( - pharmacyInvoice.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', + Utils.buildImgWithNetwork( + url: pharmacyInvoice.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/offers_and_discounts/offer_and_discounts_full_screen_swiper_page.dart b/lib/presentation/offers_and_discounts/offer_and_discounts_full_screen_swiper_page.dart index 8e7a53cd..4d377462 100644 --- a/lib/presentation/offers_and_discounts/offer_and_discounts_full_screen_swiper_page.dart +++ b/lib/presentation/offers_and_discounts/offer_and_discounts_full_screen_swiper_page.dart @@ -84,24 +84,9 @@ class _OfferAndDiscountsFullScreenSwiperPageState extends State().isArabic(), child: ClipRRect( borderRadius: BorderRadius.circular(16.r), - child: Image.asset( - _promoImages[index], + child: Utils.buildImgWithAssets( + icon: _promoImages[index], fit: BoxFit.contain, - errorBuilder: (context, error, stackTrace) { - return Container( - decoration: BoxDecoration( - color: Colors.white.withValues(alpha: 0.1), - borderRadius: BorderRadius.circular(16.r), - ), - child: Center( - child: Icon( - Icons.image_outlined, - size: 100.h, - color: Colors.white.withValues(alpha: 0.5), - ), - ), - ); - }, ), ), ), diff --git a/lib/presentation/offers_and_discounts/offers_and_discounts_detailed_page.dart b/lib/presentation/offers_and_discounts/offers_and_discounts_detailed_page.dart index 86dedbcd..ed78dffb 100644 --- a/lib/presentation/offers_and_discounts/offers_and_discounts_detailed_page.dart +++ b/lib/presentation/offers_and_discounts/offers_and_discounts_detailed_page.dart @@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart'; 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/features/offers_and_discounts/models/offers_and_discounts_response_model.dart'; @@ -92,8 +93,9 @@ class OffersAndDiscountsDetailedPage extends StatelessWidget { children: [ ClipRRect( borderRadius: BorderRadius.circular(24.h), - child: Image.network( - offer.imageUrl!, + child: Utils.buildImgWithNetwork( + url: offer.imageUrl!, + iconColor: AppColors.blackBgColor, width: double.infinity, height: 250.h, fit: BoxFit.cover, diff --git a/lib/presentation/offers_and_discounts/offers_and_discounts_page.dart b/lib/presentation/offers_and_discounts/offers_and_discounts_page.dart index a38c7935..2a26ac24 100644 --- a/lib/presentation/offers_and_discounts/offers_and_discounts_page.dart +++ b/lib/presentation/offers_and_discounts/offers_and_discounts_page.dart @@ -158,8 +158,9 @@ class _OffersAndDiscountsPageState extends State { ClipRRect( borderRadius: BorderRadius.vertical(top: Radius.circular(24.h)), child: SizedBox.expand( - child: Image.network( - offersAndDiscountVM.filteredOffers[index].imageUrl!, + child: Utils.buildImgWithNetwork( + url: offersAndDiscountVM.filteredOffers[index].imageUrl!, + iconColor: AppColors.blackBgColor, fit: BoxFit.cover, errorBuilder: (context, error, stackTrace) { return Container( diff --git a/lib/presentation/offers_and_discounts/widgets/offers_and_discounts.dart b/lib/presentation/offers_and_discounts/widgets/offers_and_discounts.dart index cfd6a86e..408831be 100644 --- a/lib/presentation/offers_and_discounts/widgets/offers_and_discounts.dart +++ b/lib/presentation/offers_and_discounts/widgets/offers_and_discounts.dart @@ -5,6 +5,8 @@ import 'package:hmg_patient_app_new/presentation/offers_and_discounts/offer_and_ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:smooth_corner/smooth_corner.dart'; +import '../../../core/utils/utils.dart'; + class OffersAndDiscountsCarousel extends StatefulWidget { const OffersAndDiscountsCarousel({super.key}); @@ -112,19 +114,9 @@ class _OffersAndDiscountsCarouselState extends State padding: const EdgeInsets.all(2.0), child: ClipRRect( borderRadius: BorderRadius.circular(11.r), - child: Image.asset( - imagePath, + child: Utils.buildImgWithAssets( + icon: imagePath, fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return Container( - color: AppColors.greyColor, - child: Icon( - Icons.image, - color: AppColors.textColorLight, - size: 32.h, - ), - ); - }, ), ), ), diff --git a/lib/presentation/parking/paking_page.dart b/lib/presentation/parking/paking_page.dart index 736ef2a0..43b25973 100644 --- a/lib/presentation/parking/paking_page.dart +++ b/lib/presentation/parking/paking_page.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/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.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/generated/locale_keys.g.dart'; @@ -110,8 +111,8 @@ class _ParkingPageState extends State { borderRadius: BorderRadius.circular(24.r), child: Transform.flip( flipX: getIt.get().isArabic(), - child: Image.asset( - AppAssets.carParkingService, + child: Utils.buildImgWithAssets( + icon: AppAssets.carParkingService, fit: BoxFit.fitHeight, height: 480.h, width: 520.w, diff --git a/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart b/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart index 4c1b13a8..a5823b4f 100644 --- a/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart +++ b/lib/presentation/prescriptions/prescription_delivery_order_summary_page.dart @@ -61,8 +61,9 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget { borderRadius: BorderRadius.all( Radius.circular(5.r), ), - child: Image.network( - prescriptionsViewModel.prescriptionDetailsList[index].imageSRCUrl!, + child: Utils.buildImgWithNetwork( + url: prescriptionsViewModel.prescriptionDetailsList[index].imageSRCUrl!, + iconColor: AppColors.blackBgColor, fit: BoxFit.cover, width: 60.w, height: 70.h, @@ -99,8 +100,9 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget { ClipRRect( clipBehavior: Clip.hardEdge, borderRadius: BorderRadius.circular(20.r), - child: Image.network( - "https://maps.googleapis.com/maps/api/staticmap?center=${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lat},${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lng}&zoom=15&size=350x165&maptype=roadmap&markers=color:red%7C${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lat},${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lng}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", + child: Utils.buildImgWithNetwork( + url: "https://maps.googleapis.com/maps/api/staticmap?center=${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lat},${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lng}&zoom=15&size=350x165&maptype=roadmap&markers=color:red%7C${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lat},${prescriptionsViewModel.locationGeocodeResponse.results.first.geometry.location.lng}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", + iconColor: AppColors.blackBgColor, fit: BoxFit.contain, ), ), diff --git a/lib/presentation/prescriptions/prescription_detail_page.dart b/lib/presentation/prescriptions/prescription_detail_page.dart index 87d51902..802c41c2 100644 --- a/lib/presentation/prescriptions/prescription_detail_page.dart +++ b/lib/presentation/prescriptions/prescription_detail_page.dart @@ -125,8 +125,9 @@ class _PrescriptionDetailPageState extends State { Row( mainAxisSize: MainAxisSize.min, children: [ - Image.network( - widget.prescriptionsResponseModel.doctorImageURL!, + Utils.buildImgWithNetwork( + url: widget.prescriptionsResponseModel.doctorImageURL!, + iconColor: AppColors.blackBgColor, width: 24.h, height: 24.h, fit: BoxFit.fill, diff --git a/lib/presentation/prescriptions/prescription_item_view.dart b/lib/presentation/prescriptions/prescription_item_view.dart index aaac5b3c..fc607986 100644 --- a/lib/presentation/prescriptions/prescription_item_view.dart +++ b/lib/presentation/prescriptions/prescription_item_view.dart @@ -42,8 +42,9 @@ class PrescriptionItemView extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, spacing: 8.h, children: [ - Image.network( - isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].imageThumbUrl!, + Utils.buildImgWithNetwork( + url: isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].imageThumbUrl!, + iconColor: AppColors.blackBgColor, width: 60.h, height: 60.h, errorBuilder: (cxt, child, tr) { diff --git a/lib/presentation/prescriptions/prescriptions_list_page.dart b/lib/presentation/prescriptions/prescriptions_list_page.dart index 1291c2da..fcb0b291 100644 --- a/lib/presentation/prescriptions/prescriptions_list_page.dart +++ b/lib/presentation/prescriptions/prescriptions_list_page.dart @@ -150,8 +150,9 @@ class _PrescriptionsListPageState extends State { Row( mainAxisSize: MainAxisSize.min, children: [ - Image.network( - model.patientPrescriptionOrders[index].doctorImageURL!, + Utils.buildImgWithNetwork( + url: model.patientPrescriptionOrders[index].doctorImageURL!, + iconColor: AppColors.blackBgColor, width: 24.h, height: 24.h, fit: BoxFit.fill, diff --git a/lib/presentation/profile_settings/accessibility_page.dart b/lib/presentation/profile_settings/accessibility_page.dart new file mode 100644 index 00000000..b879c025 --- /dev/null +++ b/lib/presentation/profile_settings/accessibility_page.dart @@ -0,0 +1,476 @@ +import 'package:easy_localization/easy_localization.dart'; +import 'package:flutter/material.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/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'; +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/appbar/collapsing_list_view.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:provider/provider.dart'; + +enum VisualMode { off, invert, dim, highContrast } + +class AccessibilityPage extends StatefulWidget { + const AccessibilityPage({super.key}); + + @override + State createState() => _AccessibilityPageState(); +} + +class _AccessibilityPageState extends State { + late ProfileSettingsViewModel profileSettingsViewModel; + int _currentFontSizeStep = 2; // 0: Small, 1: Small-Medium, 2: Medium (default), 3: Medium-Large, 4: Large + VisualMode _selectedMode = VisualMode.off; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + profileSettingsViewModel = Provider.of(context, listen: false); + // Load saved settings from ViewModel + _loadSavedSettings(); + } + }); + } + + void _loadSavedSettings() { + setState(() { + _currentFontSizeStep = profileSettingsViewModel.fontSizeStep; + _selectedMode = _visualModeFromString(profileSettingsViewModel.visualMode); + }); + } + + VisualMode _visualModeFromString(String mode) { + switch (mode) { + case 'off': + return VisualMode.off; + case 'invert': + return VisualMode.invert; + case 'dim': + return VisualMode.dim; + case 'highContrast': + return VisualMode.highContrast; + default: + return VisualMode.off; + } + } + + String _visualModeToString(VisualMode mode) { + switch (mode) { + case VisualMode.off: + return 'off'; + case VisualMode.invert: + return 'invert'; + case VisualMode.dim: + return 'dim'; + case VisualMode.highContrast: + return 'highContrast'; + } + } + + @override + void dispose() { + super.dispose(); + } + + double _getFontSizeForStep(int step) { + const double mediumSize = 40.0; // Medium is baseline + switch (step) { + case 0: + return mediumSize * 0.5; // Small: 20.0 + case 1: + return mediumSize * 0.75; // Small-Medium: 30.0 + case 2: + return mediumSize; // Medium (default): 40.0 + case 3: + return mediumSize * 1.5; // Medium-Large: 60.0 + case 4: + return mediumSize * 1.75; // Large: 70.0 + default: + return mediumSize; + } + } + + @override + Widget build(BuildContext context) { + profileSettingsViewModel = Provider.of(context, listen: false); + + return CollapsingListView( + title: LocaleKeys.accessibility.tr(context: context), + isClose: true, + bottomChild: Container( + padding: EdgeInsets.all(24.w), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: false, + ), + child: CustomButton( + height: 56.h, + icon: AppAssets.checkmark_icon, + iconColor: AppColors.whiteColor, + text: LocaleKeys.saveChanges.tr(context: context), + backgroundColor: AppColors.primaryRedColor, + borderColor: AppColors.primaryRedColor, + textColor: AppColors.whiteColor, + fontSize: 16.f, + isBold: true, + onPressed: () { + // Save accessibility settings to ViewModel + profileSettingsViewModel.setFontSizeStep(_currentFontSizeStep); + profileSettingsViewModel.setVisualMode(_visualModeToString(_selectedMode)); + + // Show success message + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: LocaleKeys.settingsSavedSuccessfully.tr(context: context).toText14( + color: AppColors.whiteColor, + ), + backgroundColor: AppColors.successColor, + duration: const Duration(seconds: 2), + ), + ); + + // Pop the page + Navigator.pop(context); + }, + ), + ), + child: SingleChildScrollView( + padding: EdgeInsets.only(top: 16.h, bottom: 24.h, left: 24.w, right: 24.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Font Size Card + _buildFontSizeCard(), + SizedBox(height: 24.h), + + // Mode for Partially Blind Card + _buildVisualModeCard(), + ], + ), + ), + ); + } + + Widget _buildFontSizeCard() { + return Container( + padding: EdgeInsets.all(24.w), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: false, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Card Label + LocaleKeys.fontSize.tr(context: context).toText18(isBold: true), + SizedBox(height: 24.h), + + // Center Aa heading that changes size + Container( + height: 70.h, + child: Center( + child: AnimatedDefaultTextStyle( + duration: const Duration(milliseconds: 300), + style: TextStyle( + fontSize: _getFontSizeForStep(_currentFontSizeStep), + fontWeight: FontWeight.bold, + color: AppColors.blackBgColor, + ), + child: const Text("Aa"), + ), + ), + ), + SizedBox(height: 32.h), + + // Stepped slider + Column( + children: [ + // Stack to position circles on top of the bar + Stack( + alignment: Alignment.center, + children: [ + // Grey bar + Container( + height: 4.h, + decoration: BoxDecoration( + color: AppColors.greyTextColor.withValues(alpha: 0.2), + borderRadius: BorderRadius.circular(2.r), + ), + ), + // Step indicator with dots positioned on top of the bar + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: List.generate(5, (index) { + bool isSelected = index == _currentFontSizeStep; + return GestureDetector( + onTap: () { + setState(() { + _currentFontSizeStep = index; + }); + }, + behavior: HitTestBehavior.opaque, + child: Padding( + padding: EdgeInsets.all(8.w), // Increased tap zone + child: Container( + width: isSelected ? 20.w : 12.w, + height: isSelected ? 20.w : 12.w, + decoration: BoxDecoration( + color: isSelected ? AppColors.primaryRedColor : Colors.transparent, + shape: BoxShape.circle, + border: Border.all( + color: isSelected ? AppColors.primaryRedColor : AppColors.greyTextColor.withValues(alpha: 0.3), + width: 2.w, + ), + ), + ), + ), + ); + }), + ), + ], + ), + SizedBox(height: 12.h), + + // Labels + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + LocaleKeys.fontSizeSmall.tr(context: context).toText12(color: AppColors.greyTextColor), + LocaleKeys.fontSizeMedium.tr(context: context).toText12(color: AppColors.greyTextColor), + LocaleKeys.fontSizeLarge.tr(context: context).toText12(color: AppColors.greyTextColor), + ], + ), + ], + ), + ], + ), + ); + } + + Widget _buildVisualModeCard() { + return Container( + padding: EdgeInsets.all(24.w), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 24.r, + hasShadow: false, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Card Label + LocaleKeys.blindModes.tr(context: context).toText18(isBold: true), + SizedBox(height: 24.h), + + // Two columns layout + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Left Column - Radio buttons + Expanded( + flex: 2, + child: Column( + children: [ + _buildRadioOption(VisualMode.off, LocaleKeys.offTheme.tr(context: context)), + SizedBox(height: 12.h), + _buildRadioOption(VisualMode.invert, LocaleKeys.invertTheme.tr(context: context)), + SizedBox(height: 12.h), + _buildRadioOption(VisualMode.dim, LocaleKeys.dimTheme.tr(context: context)), + SizedBox(height: 12.h), + _buildRadioOption(VisualMode.highContrast, LocaleKeys.highContrastTheme.tr(context: context)), + ], + ), + ), + SizedBox(width: 16.w), + + // Right Column - Preview Card + Expanded( + flex: 3, + child: _buildPreviewCard(), + ), + ], + ), + ], + ), + ); + } + + Widget _buildRadioOption(VisualMode mode, String label) { + bool isSelected = _selectedMode == mode; + return GestureDetector( + onTap: () { + setState(() { + _selectedMode = mode; + }); + }, + child: Row( + children: [ + Container( + width: 20.w, + height: 20.w, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: isSelected ? AppColors.primaryRedColor : AppColors.greyTextColor, + width: 2.w, + ), + color: Colors.transparent, + ), + child: isSelected + ? Center( + child: Container( + width: 10.w, + height: 10.w, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: AppColors.primaryRedColor, + ), + ), + ) + : null, + ), + SizedBox(width: 8.w), + label.toText14( + isBold: isSelected, + color: isSelected ? AppColors.blackBgColor : AppColors.greyTextColor, + ), + ], + ), + ); + } + + Widget _buildPreviewCard() { + // Always build preview content with base "Off" mode colors + Widget previewContent = Container( + padding: EdgeInsets.all(16.w), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 20.r, + isCustomShadow: [ + BoxShadow( + color: const Color(0xff000000).withValues(alpha: 0.1), + blurRadius: 16, + spreadRadius: 16 * 0.1, // 10% of blur radius + offset: const Offset(0, 0), // Uniform shadow on all sides + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Top row with heading and arrow + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + LocaleKeys.cardHeading.tr(context: context).toText14( + isBold: true, + color: AppColors.blackBgColor, // Always use base color + ), + Transform.scale( + scaleX: Utils.appState.isArabic() ? -1 : 1, + child: Utils.buildSvgWithAssets( + icon: AppAssets.arrow_forward, + iconColor: AppColors.blackBgColor, // Always use base color + ), + ), + ], + ), + SizedBox(height: 16.h), + + // Price with SAR symbol + Row( + children: [ + "1234".toText24( + isBold: true, + color: AppColors.blackBgColor, // Always use base color + ), + SizedBox(width: 4.w), + LocaleKeys.riyal.tr(context: context).toText14( + color: AppColors.blackBgColor, // Always use base color + ), + SizedBox(width: 4.w), + Utils.buildSvgWithAssets( + icon: AppAssets.saudi_riyal_icon, + iconColor: AppColors.blackBgColor, // Always use base color + width: 16.w, + height: 16.w, + ), + ], + ), + SizedBox(height: 16.h), + + // Button - always show with base "Off" colors + CustomButton( + height: 44.h, + icon: AppAssets.add_icon, + iconColor: AppColors.whiteColor, + // Always use base color + text: LocaleKeys.buttonText.tr(context: context), + backgroundColor: AppColors.primaryRedColor, + // Always use base color + borderColor: AppColors.primaryRedColor, + // Always use base color + textColor: AppColors.whiteColor, + // Always use base color + fontSize: 14.f, + onPressed: () {}, + ), + ], + ), + ); + + // Apply visual mode filters to preview + switch (_selectedMode) { + case VisualMode.invert: + return ColorFiltered( + colorFilter: const ColorFilter.matrix([ + -1, 0, 0, 0, 255, // Red channel inverted + 0, -1, 0, 0, 255, // Green channel inverted + 0, 0, -1, 0, 255, // Blue channel inverted + 0, 0, 0, 1, 0, // Alpha channel unchanged + ]), + child: previewContent, + ); + + case VisualMode.dim: + return Stack( + children: [ + previewContent, + Positioned.fill( + child: IgnorePointer( + child: Container( + decoration: BoxDecoration( + color: AppColors.blackBgColor.withValues(alpha: 0.3), + borderRadius: BorderRadius.circular(20.r), + ), + ), + ), + ), + ], + ); + + case VisualMode.highContrast: + return ColorFiltered( + colorFilter: const ColorFilter.matrix([ + 0.2126, 0.7152, 0.0722, 0, 0, // Red channel (grayscale) + 0.2126, 0.7152, 0.0722, 0, 0, // Green channel (grayscale) + 0.2126, 0.7152, 0.0722, 0, 0, // Blue channel (grayscale) + 0, 0, 0, 1, 0, // Alpha channel unchanged + ]), + child: previewContent, + ); + + case VisualMode.off: + return previewContent; + } + } +} diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart index a1b1e367..ee48b95e 100644 --- a/lib/presentation/profile_settings/profile_settings.dart +++ b/lib/presentation/profile_settings/profile_settings.dart @@ -23,6 +23,7 @@ import 'package:hmg_patient_app_new/features/profile_settings/profile_settings_v import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart'; import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart'; +import 'package:hmg_patient_app_new/presentation/profile_settings/accessibility_page.dart'; import 'package:hmg_patient_app_new/presentation/profile_settings/widgets/profile_picture_widget.dart'; import 'package:hmg_patient_app_new/presentation/profile_settings/widgets/update_email_widget.dart'; import 'package:hmg_patient_app_new/presentation/profile_settings/widgets/update_emergency_contact_widget.dart'; @@ -254,6 +255,10 @@ class ProfileSettingsState extends State { ); }, ), + 1.divider, + actionItem(AppAssets.bell, LocaleKeys.accessibility.tr(context: context), () { + Navigator.of(context).push(CustomPageRoute(page: const AccessibilityPage())); + }), ], ), ), @@ -600,7 +605,13 @@ class _FamilyCardWidgetState extends State { ], ).expanded, // Icon(Icons.qr_code, size: 56.h) - Image.network("https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=${widget.profile.responseId.toString()}", fit: BoxFit.contain, height: 56.h, width: 56.w) + Utils.buildImgWithNetwork( + url: "https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=${widget.profile.responseId.toString()}", + iconColor: AppColors.transparent, + fit: BoxFit.contain, + height: 56.h, + width: 56.w, + ) ], ), SizedBox(height: 4.h), diff --git a/lib/presentation/profile_settings/widgets/profile_picture_widget.dart b/lib/presentation/profile_settings/widgets/profile_picture_widget.dart index b5aa89ee..5fe71e1e 100644 --- a/lib/presentation/profile_settings/widgets/profile_picture_widget.dart +++ b/lib/presentation/profile_settings/widgets/profile_picture_widget.dart @@ -128,12 +128,10 @@ class _ProfilePictureWidgetState extends State { // Show default image (no image data or user has no uploaded image) print('📷 Showing default avatar for user $currentPatientId'); - return Image.asset( - defaultImage, - key: ValueKey('default_$currentPatientId'), + return Utils.buildImgWithAssets( + icon: defaultImage, width: 136.w, height: 136.h, - gaplessPlayback: true, ); } diff --git a/lib/presentation/radiology/radiology_ai_analysis_widget.dart b/lib/presentation/radiology/radiology_ai_analysis_widget.dart index 8daf53e0..f22bcbd0 100644 --- a/lib/presentation/radiology/radiology_ai_analysis_widget.dart +++ b/lib/presentation/radiology/radiology_ai_analysis_widget.dart @@ -10,6 +10,7 @@ import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.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'; +import 'package:hmg_patient_app_new/widgets/accessibility/preserve_svg_colors.dart'; class RadiologyAiAnalysisWidget extends StatelessWidget { final RadiologyAiAnalysisResponse data; @@ -47,7 +48,7 @@ class RadiologyAiAnalysisWidget extends StatelessWidget { Container( padding: EdgeInsets.all(8.r), decoration: BoxDecoration(gradient: AppColors.aiLinearGradient, borderRadius: BorderRadius.circular(8.r)), - child: SvgPicture.asset(AppAssets.aiOverView, colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), height: 18.r, width: 18.r), + child: PreserveSvgColors(child: SvgPicture.asset(AppAssets.aiOverView, colorFilter: const ColorFilter.mode(Colors.white, BlendMode.srcIn), height: 18.r, width: 18.r)), ), ], ), diff --git a/lib/presentation/radiology/radiology_orders_page.dart b/lib/presentation/radiology/radiology_orders_page.dart index e8c1cedc..3ff500db 100644 --- a/lib/presentation/radiology/radiology_orders_page.dart +++ b/lib/presentation/radiology/radiology_orders_page.dart @@ -401,8 +401,9 @@ class _RadiologyOrdersPageState extends State { Row( mainAxisSize: MainAxisSize.min, children: [ - Image.network( - group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + Utils.buildImgWithNetwork( + url: group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", + iconColor: AppColors.transparent, width: 24.w, height: 24.h, fit: BoxFit.cover, diff --git a/lib/presentation/rate_appointment/rate_appointment_clinic.dart b/lib/presentation/rate_appointment/rate_appointment_clinic.dart index e7e29f67..c8aedea8 100644 --- a/lib/presentation/rate_appointment/rate_appointment_clinic.dart +++ b/lib/presentation/rate_appointment/rate_appointment_clinic.dart @@ -1,7 +1,7 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.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/features/my_appointments/appointment_rating_view_model.dart'; @@ -96,7 +96,13 @@ class _RateAppointmentClinicState extends State { }); }, iconSize: 35, - icon: SvgPicture.asset('assets/images/svg/rate_${index + 1}.svg', colorFilter: getColors(rating)), + icon: Utils.buildSvgWithAssets( + icon: 'assets/images/svg/rate_${index + 1}.svg', + iconColor: getRatingColor(rating), + width: 35, + height: 35, + applyThemeColor: false, + ), ), ) : IconButton( @@ -106,7 +112,12 @@ class _RateAppointmentClinicState extends State { }); }, iconSize: 35, - icon: SvgPicture.asset('assets/images/svg/rate_${index + 1}.svg'), + icon: Utils.buildSvgWithAssets( + icon: 'assets/images/svg/rate_${index + 1}.svg', + width: 35, + height: 35, + applyThemeColor: false, + ), ), ), ], @@ -181,6 +192,23 @@ class _RateAppointmentClinicState extends State { ); } + Color getRatingColor(int rating) { + switch (rating) { + case 5: + return AppColors.bgGreenColor; + case 4: + return Colors.greenAccent; + case 3: + return AppColors.warningLightColor; + case 2: + return Colors.orange; + case 1: + return AppColors.primaryRedColor; + default: + return AppColors.greyColor; + } + } + ColorFilter getColors(int rating){ switch(rating){ diff --git a/lib/presentation/rate_appointment/widget/doctor_row.dart b/lib/presentation/rate_appointment/widget/doctor_row.dart index 2a139d05..09b6c134 100644 --- a/lib/presentation/rate_appointment/widget/doctor_row.dart +++ b/lib/presentation/rate_appointment/widget/doctor_row.dart @@ -9,6 +9,9 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'dart:ui' as ui; +import '../../../core/utils/utils.dart'; +import '../../../theme/colors.dart'; + class BuildDoctorRow extends StatelessWidget { bool isForClinic = false; AppointmentDetails? appointmentDetails; @@ -21,8 +24,9 @@ class BuildDoctorRow extends StatelessWidget { return Padding(padding: EdgeInsets.all(16),child:Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.network( - isForClinic ? 'https://hmgwebservices.com/Images/Hospitals/${appointmentDetails!.projectID}.jpg' : appointmentDetails!.doctorImageURL , + Utils.buildImgWithNetwork( + url: isForClinic ? 'https://hmgwebservices.com/Images/Hospitals/${appointmentDetails!.projectID}.jpg' : appointmentDetails!.doctorImageURL , + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/smartwatches/smartwatch_home_page.dart b/lib/presentation/smartwatches/smartwatch_home_page.dart index 7367af54..3bd8e9f5 100644 --- a/lib/presentation/smartwatches/smartwatch_home_page.dart +++ b/lib/presentation/smartwatches/smartwatch_home_page.dart @@ -81,7 +81,7 @@ class SmartwatchHomePage extends StatelessWidget { ), child: Column( children: [ - Image.asset("assets/images/png/smartwatches/apple-watch-5.jpg", width: 136.h, height: 136.h).paddingSymmetrical(24.w, 8.h), + Utils.buildImgWithAssets(icon: "assets/images/png/smartwatches/apple-watch-5.jpg", width: 136.h, height: 136.h).paddingSymmetrical(24.w, 8.h), "Apple Watch".needTranslation.toText16(isBold: true), CustomButton( text: LocaleKeys.selectSmartWatch.tr(context: context), @@ -115,7 +115,7 @@ class SmartwatchHomePage extends StatelessWidget { decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), child: Column( children: [ - Image.asset("assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg", fit: BoxFit.contain, width: 136.w, height: 136.h) + Utils.buildImgWithAssets(icon: "assets/images/png/smartwatches/galaxy_watch_8_classic.jpeg", fit: BoxFit.contain, width: 136.w, height: 136.h) .paddingSymmetrical(24.w, 8.h), "Samsung Watch".needTranslation.toText16(isBold: true), CustomButton( @@ -153,7 +153,7 @@ class SmartwatchHomePage extends StatelessWidget { ), child: Column( children: [ - Image.asset("assets/images/png/smartwatches/Huawei_Watch.png", width: 136.w, height: 136.h).paddingSymmetrical(24.w, 8.h), + Utils.buildImgWithAssets(icon: "assets/images/png/smartwatches/Huawei_Watch.png", width: 136.w, height: 136.h).paddingSymmetrical(24.w, 8.h), "Huawei Watch".needTranslation.toText16(isBold: true), CustomButton( text: LocaleKeys.selectSmartWatch.tr(context: context), @@ -187,7 +187,7 @@ class SmartwatchHomePage extends StatelessWidget { ), child: Column( children: [ - Image.asset("assets/images/png/smartwatches/Whoop_Watch.png", width: 136.w, height: 136.h).paddingSymmetrical(24.w, 8.h), + Utils.buildImgWithAssets(icon: "assets/images/png/smartwatches/Whoop_Watch.png", width: 136.w, height: 136.h).paddingSymmetrical(24.w, 8.h), "Whoop Watch".needTranslation.toText16(isBold: true), CustomButton( text: LocaleKeys.selectSmartWatch.tr(context: context), diff --git a/lib/presentation/smartwatches/smartwatch_instructions_page.dart b/lib/presentation/smartwatches/smartwatch_instructions_page.dart index a4f6c0c0..7c0d0b97 100644 --- a/lib/presentation/smartwatches/smartwatch_instructions_page.dart +++ b/lib/presentation/smartwatches/smartwatch_instructions_page.dart @@ -48,8 +48,8 @@ class SmartwatchInstructionsPage extends StatelessWidget { mainAxisSize: MainAxisSize.max, spacing: 18.h, children: [ - Image.asset( - smartwatchDetails.watchIcon, + Utils.buildImgWithAssets( + icon: smartwatchDetails.watchIcon, fit: BoxFit.contain, height: 280.h, width: 280.w, diff --git a/lib/presentation/smartwatches/widgets/health_metric_card.dart b/lib/presentation/smartwatches/widgets/health_metric_card.dart index 023f41b7..53ad4705 100644 --- a/lib/presentation/smartwatches/widgets/health_metric_card.dart +++ b/lib/presentation/smartwatches/widgets/health_metric_card.dart @@ -1,11 +1,11 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:health/health.dart'; import 'package:fl_chart/fl_chart.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.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/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/smartwatches/widgets/health_metric.dart'; @@ -53,12 +53,12 @@ class HealthMetricCard extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SvgPicture.asset( - metric.svgIcon, + Utils.buildSvgWithAssets( + icon: metric.svgIcon, height: 30.h, width: 30.h, fit: BoxFit.contain, - // color: color, + applyThemeColor: false, ), // Icon( // metric.icon, diff --git a/lib/presentation/symptoms_checker/widgets/interactive_body_widget.dart b/lib/presentation/symptoms_checker/widgets/interactive_body_widget.dart index ee2370d1..a32709f9 100644 --- a/lib/presentation/symptoms_checker/widgets/interactive_body_widget.dart +++ b/lib/presentation/symptoms_checker/widgets/interactive_body_widget.dart @@ -2,6 +2,8 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.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/features/symptoms_checker/models/organ_model.dart'; import 'package:hmg_patient_app_new/presentation/symptoms_checker/widgets/organ_dot_widget.dart'; import 'package:hmg_patient_app_new/presentation/symptoms_checker/widgets/organ_tooltip_widget.dart'; @@ -113,11 +115,21 @@ class _InteractiveBodyWidgetState extends State { children: [ // Body image Positioned.fill( - child: Image.asset( - widget.bodyImageAsset, - fit: BoxFit.fill, - color: widget.isBodyHidden ? Colors.black.withValues(alpha: 0.5) : null, - ), + child: widget.isBodyHidden + ? ColorFiltered( + colorFilter: ColorFilter.mode( + Colors.black.withValues(alpha: 0.5), + BlendMode.darken, + ), + child: Utils.buildImgWithAssets( + icon: widget.bodyImageAsset, + fit: BoxFit.fill, + ), + ) + : Utils.buildImgWithAssets( + icon: widget.bodyImageAsset, + fit: BoxFit.fill, + ), ), // Organ dots diff --git a/lib/presentation/tele_consultation/zoom/call_screen.dart b/lib/presentation/tele_consultation/zoom/call_screen.dart index 82ae6332..64c6c471 100644 --- a/lib/presentation/tele_consultation/zoom/call_screen.dart +++ b/lib/presentation/tele_consultation/zoom/call_screen.dart @@ -1779,7 +1779,7 @@ class _CallScreenState extends State { children: [ IconButton( onPressed: onPressAudio, - icon: isMuted.value ? Image.asset("assets/images/png/zoom/unmute@2x.png") : Image.asset("assets/images/png/zoom/mute@2x.png"), + icon: isMuted.value ? Utils.buildImgWithAssets(icon: "assets/images/png/zoom/unmute@2x.png") : Utils.buildImgWithAssets(icon: "assets/images/png/zoom/mute@2x.png"), iconSize: circleButtonSize, tooltip: isMuted.value == true ? "Unmute" : "Mute", ), @@ -1791,7 +1791,7 @@ class _CallScreenState extends State { IconButton( onPressed: onPressVideo, iconSize: circleButtonSize, - icon: isVideoOn.value ? Image.asset("assets/images/png/zoom/video-off@2x.png") : Image.asset("assets/images/png/zoom/video-on@2x.png"), + icon: isVideoOn.value ? Utils.buildImgWithAssets(icon: "assets/images/png/zoom/video-off@2x.png") : Utils.buildImgWithAssets(icon: "assets/images/png/zoom/video-on@2x.png"), ), Column( children: [ diff --git a/lib/presentation/todo_section/ancillary_order_payment_page.dart b/lib/presentation/todo_section/ancillary_order_payment_page.dart index e48adeeb..8cdd0c82 100644 --- a/lib/presentation/todo_section/ancillary_order_payment_page.dart +++ b/lib/presentation/todo_section/ancillary_order_payment_page.dart @@ -107,7 +107,7 @@ class _AncillaryOrderPaymentPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset(AppAssets.mada, width: 50.h, height: 50.h), + Utils.buildImgWithAssets(icon: AppAssets.mada, width: 50.h, height: 50.h), SizedBox(height: 16.h), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: todoVM.isProcessingPayment), ], @@ -177,9 +177,9 @@ class _AncillaryOrderPaymentPageState extends State { children: [ Row( children: [ - Image.asset(AppAssets.visa, width: 50.h, height: 50.h), + Utils.buildImgWithAssets(icon: AppAssets.visa, width: 50.h, height: 50.h), SizedBox(width: 8.h), - Image.asset(AppAssets.mastercard, width: 40.h, height: 40.h), + Utils.buildImgWithAssets(icon: AppAssets.mastercard, width: 40.h, height: 40.h), ], ).toShimmer2(isShow: todoVM.isProcessingPayment), SizedBox(height: 16.h), diff --git a/lib/presentation/todo_section/ancillary_procedures_details_page.dart b/lib/presentation/todo_section/ancillary_procedures_details_page.dart index 65e1bdaf..fdcdad9a 100644 --- a/lib/presentation/todo_section/ancillary_procedures_details_page.dart +++ b/lib/presentation/todo_section/ancillary_procedures_details_page.dart @@ -224,8 +224,8 @@ class _AncillaryOrderDetailsListState extends State { Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Image.asset( - gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, + Utils.buildImgWithAssets( + icon: gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, width: 56.w, height: 56.h, ), diff --git a/lib/presentation/todo_section/widgets/ancillary_orders_list.dart b/lib/presentation/todo_section/widgets/ancillary_orders_list.dart index 627642ef..cc5bbbb6 100644 --- a/lib/presentation/todo_section/widgets/ancillary_orders_list.dart +++ b/lib/presentation/todo_section/widgets/ancillary_orders_list.dart @@ -138,8 +138,9 @@ class AncillaryOrderCard extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ if (!isLoading) ...[ - Image.network( - "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", + Utils.buildImgWithNetwork( + url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", + iconColor: AppColors.transparent, width: 63.h, height: 63.h, fit: BoxFit.cover, diff --git a/lib/presentation/vital_sign/vital_sign_page.dart b/lib/presentation/vital_sign/vital_sign_page.dart index fb3f1a58..826c3971 100644 --- a/lib/presentation/vital_sign/vital_sign_page.dart +++ b/lib/presentation/vital_sign/vital_sign_page.dart @@ -235,12 +235,11 @@ class _VitalSignPageState extends State { child: Stack( fit: StackFit.expand, children: [ - Image.asset( - getIt.get().getAuthenticatedUser()?.gender == 1 + Utils.buildImgWithAssets( + icon: getIt.get().getAuthenticatedUser()?.gender == 1 ? AppAssets.bmiFullBody : AppAssets.bmiFullBodyFemale, fit: BoxFit.cover, - alignment: Alignment.topCenter, ), Align( alignment: Alignment.bottomCenter, diff --git a/lib/widgets/buttons/default_button.dart b/lib/widgets/buttons/default_button.dart index f6a47a1c..9d62ff6f 100644 --- a/lib/widgets/buttons/default_button.dart +++ b/lib/widgets/buttons/default_button.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.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/theme/colors.dart'; extension WithContainer on Widget { @@ -63,7 +63,7 @@ class DefaultButton extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ if (iconData != null) Icon(iconData, color: textColor), - if (svgIcon != null) SvgPicture.asset(svgIcon ?? ""), + if (svgIcon != null) Utils.buildSvgWithAssets(icon: svgIcon ?? "", applyThemeColor: false), if (!isTextExpanded) Padding( padding: EdgeInsets.only( diff --git a/lib/widgets/chip/app_custom_chip_widget.dart b/lib/widgets/chip/app_custom_chip_widget.dart index 734ca2cc..00bf6df1 100644 --- a/lib/widgets/chip/app_custom_chip_widget.dart +++ b/lib/widgets/chip/app_custom_chip_widget.dart @@ -78,7 +78,7 @@ class AppCustomChipWidget extends StatelessWidget { ? Chip( avatar: icon.isNotEmpty ? isIconPNG - ? Image.asset(icon, width: iconS, height: iconS) + ? Utils.buildImgWithAssets(icon: icon, width: iconS, height: iconS) : Utils.buildSvgWithAssets( icon: icon, width: iconS, diff --git a/lib/widgets/image_picker.dart b/lib/widgets/image_picker.dart index 6ab2b439..4074bd19 100644 --- a/lib/widgets/image_picker.dart +++ b/lib/widgets/image_picker.dart @@ -79,7 +79,7 @@ class ImageOptions { } }, onFilesTap: () async { - FilePickerResult? result = await FilePicker.platform.pickFiles( + FilePickerResult? result = await FilePicker.pickFiles( type: FileType.custom, allowedExtensions: [ 'jpg', diff --git a/lib/widgets/map/location_map_widget.dart b/lib/widgets/map/location_map_widget.dart index da17b486..f3b57aa3 100644 --- a/lib/widgets/map/location_map_widget.dart +++ b/lib/widgets/map/location_map_widget.dart @@ -12,6 +12,8 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:map_launcher/map_launcher.dart'; +import '../../core/utils/utils.dart'; + /// A reusable location map widget that displays a static map with address and directions button /// Can be used in any review screen (HHC, CMC, etc.) class LocationMapWidget extends StatelessWidget { @@ -127,24 +129,10 @@ class LocationMapWidget extends StatelessWidget { ClipRRect( clipBehavior: Clip.hardEdge, borderRadius: BorderRadius.circular(24.r), - child: Image.network( - staticMapUrl, + child: Utils.buildImgWithNetwork( + url: staticMapUrl, + iconColor: AppColors.transparent, fit: BoxFit.contain, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return Container( - height: 165.h, - decoration: BoxDecoration( - color: AppColors.bgScaffoldColor, - borderRadius: BorderRadius.circular(24.r), - ), - child: Center( - child: CircularProgressIndicator( - color: AppColors.primaryRedColor, - ), - ), - ); - }, errorBuilder: (context, error, stackTrace) { return Container( height: 165.h, diff --git a/lib/widgets/media_viewer/full_screen_image_viewer.dart b/lib/widgets/media_viewer/full_screen_image_viewer.dart index 0fb92f0e..ae6d4802 100644 --- a/lib/widgets/media_viewer/full_screen_image_viewer.dart +++ b/lib/widgets/media_viewer/full_screen_image_viewer.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; class FullScreenImageViewer extends StatelessWidget { @@ -22,8 +22,8 @@ class FullScreenImageViewer extends StatelessWidget { body: Center( child: InteractiveViewer( child: isSvg - ? SvgPicture.asset(path, width: double.infinity, fit: BoxFit.contain) - : Image.asset(width: double.infinity, path, fit: BoxFit.contain), + ? Utils.buildSvgWithAssets(icon: path, width: double.infinity, fit: BoxFit.contain, applyThemeColor: false) + : Utils.buildImgWithAssets(icon: path, width: double.infinity, fit: BoxFit.contain), ), ), ); diff --git a/lib/widgets/nfc/nfc_reader_sheet.dart b/lib/widgets/nfc/nfc_reader_sheet.dart index 0d6c2866..8f983156 100644 --- a/lib/widgets/nfc/nfc_reader_sheet.dart +++ b/lib/widgets/nfc/nfc_reader_sheet.dart @@ -3,9 +3,9 @@ import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_nfc_kit/flutter_nfc_kit.dart'; -import 'package:flutter_svg/flutter_svg.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/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'; @@ -93,9 +93,10 @@ class _NfcLayoutState extends State { SizedBox( height: 30, ), - SvgPicture.asset( - "assets/images/nfc/contactless.svg", + Utils.buildSvgWithAssets( + icon: "assets/images/nfc/contactless.svg", height: MediaQuery.of(context).size.width / 3, + applyThemeColor: false, ), SizedBox( height: 30, @@ -161,8 +162,8 @@ class _NfcLayoutState extends State { SizedBox( height: 30, ), - Image.asset( - "assets/images/nfc/ic_done.png", + Utils.buildImgWithAssets( + icon: "assets/images/nfc/ic_done.png", height: MediaQuery.of(context).size.width / 3, ), SizedBox( diff --git a/lib/widgets/user_avatar_widget.dart b/lib/widgets/user_avatar_widget.dart index 84499efa..5528531d 100644 --- a/lib/widgets/user_avatar_widget.dart +++ b/lib/widgets/user_avatar_widget.dart @@ -4,6 +4,7 @@ import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; class UserAvatarWidget extends StatelessWidget { final double width; @@ -84,11 +85,11 @@ class UserAvatarWidget extends StatelessWidget { // If decoding fails, show default image return ClipRRect( borderRadius: BorderRadius.circular(effectiveBorderRadius), - child: Image.asset( - defaultImage, + child: Utils.buildImgWithAssets( + icon: defaultImage, width: width, height: height, - fit: fit, + fit: fit ?? BoxFit.cover, ), ); } @@ -97,11 +98,11 @@ class UserAvatarWidget extends StatelessWidget { // Show default image with rounded corners return ClipRRect( borderRadius: BorderRadius.circular(effectiveBorderRadius), - child: Image.asset( - defaultImage, + child: Utils.buildImgWithAssets( + icon: defaultImage, width: width, height: height, - fit: fit, + fit: fit ?? BoxFit.cover, ), ); } -- 2.30.2