Merge pull request 'Accessibility & Image Widgets Update' (#328) from dev_aamir into master

Reviewed-on: https://34.17.182.140/Haroon6138/HMG_Patient_App_New/pulls/328
dev_sultan
Haroon6138 3 weeks ago
commit 821bc578c9

@ -683,11 +683,20 @@
"emrgNo": "رقم جهة الاتصال في حالات الطوارئ", "emrgNo": "رقم جهة الاتصال في حالات الطوارئ",
"modes": "الأوضاع", "modes": "الأوضاع",
"vibration": "اهتزاز عند اللمس", "vibration": "اهتزاز عند اللمس",
"blindModes": "أوضاع للمكفوفين جزئيًا", "blindModes": "وضع للمكفوفين جزئياً",
"invertTheme": "عكس", "invertTheme": "عكس",
"offTheme": "إيقاف", "offTheme": "إيقاف",
"dimTheme": "خافت", "dimTheme": "خافت",
"bwTheme": "أسود وأبيض", "bwTheme": "أسود وأبيض",
"highContrastTheme": "تباين عالي",
"fontSize": "حجم الخط",
"fontSizeSmall": "صغير",
"fontSizeMedium": "متوسط",
"fontSizeLarge": "كبير",
"saveChanges": "حفظ التغييرات",
"settingsSavedSuccessfully": "تم حفظ الإعدادات بنجاح",
"cardHeading": "عنوان البطاقة",
"buttonText": "نص الزر",
"permissions": "إذن", "permissions": "إذن",
"cameraPermission": "الكاميرا", "cameraPermission": "الكاميرا",
"locationPermission": "الموقع", "locationPermission": "الموقع",
@ -695,7 +704,7 @@
"outOfStockMsg": "لقد أضفت منتجًا غير متوفر حاليًا، يرجى إزالته!", "outOfStockMsg": "لقد أضفت منتجًا غير متوفر حاليًا، يرجى إزالته!",
"noArabicLetters": "لا يُسمح باستخدام الحروف العربية", "noArabicLetters": "لا يُسمح باستخدام الحروف العربية",
"noOffersAvailable": "لا توجد عروض متاحة!", "noOffersAvailable": "لا توجد عروض متاحة!",
"accessibility": "وضع الوصول", "accessibility": "إمكانية الوصول",
"orderStatus": "حالة الطلب", "orderStatus": "حالة الطلب",
"findUs": "فروعنا", "findUs": "فروعنا",
"liveChat": "محادثة مباشرة", "liveChat": "محادثة مباشرة",

@ -673,11 +673,20 @@
"emrgNo": "Emergency Contact Number", "emrgNo": "Emergency Contact Number",
"modes": "Modes", "modes": "Modes",
"vibration": "Vibration Touch Feedback", "vibration": "Vibration Touch Feedback",
"blindModes": "Modes for Partially Blind", "blindModes": "Mode for partially blind",
"invertTheme": "Invert", "invertTheme": "Invert",
"offTheme": "Off", "offTheme": "Off",
"dimTheme": "Dim", "dimTheme": "Dim",
"bwTheme": "Black and White", "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", "permissions": "Permission",
"cameraPermission": "Camera", "cameraPermission": "Camera",
"locationPermission": "Location", "locationPermission": "Location",
@ -685,7 +694,7 @@
"outOfStockMsg": "You have added product which is out of stock now, Please remove that!", "outOfStockMsg": "You have added product which is out of stock now, Please remove that!",
"noArabicLetters": "It Is Not Allow to Use Arabic Letters", "noArabicLetters": "It Is Not Allow to Use Arabic Letters",
"noOffersAvailable": "No Offers Available!", "noOffersAvailable": "No Offers Available!",
"accessibility": "Accessibility Mode", "accessibility": "Accessibility",
"orderStatus": "Order Status", "orderStatus": "Order Status",
"findUs": "Find Us", "findUs": "Find Us",
"liveChat": "Live Chat", "liveChat": "Live Chat",

@ -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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/dialogs/confirm_dialog.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/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:lottie/lottie.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -120,10 +122,8 @@ class Utils {
// ), // ),
// )); // ));
return !isAddHours return !isAddHours
? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US") ? DateFormat('hh:mm a', appState.isArabic() ? "ar_SA" : "en_US").format(DateTime.tryParse(startTime.contains("T") ? startTime : convertStringToDateTime(startTime))!.toLocal())
.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))!.add(
Duration( Duration(
hours: isAddHours ? 3 : 0, hours: isAddHours ? 3 : 0,
), ),
@ -356,12 +356,9 @@ class Utils {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
SizedBox(height: isSmallWidget ? 0.h : 48.h), SizedBox(height: isSmallWidget ? 0.h : 48.h),
Lottie.asset(AppAnimations.noData, Lottie.asset(AppAnimations.noData, repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill),
repeat: false, reverse: false, frameRate: FrameRate(60), width: width.w, height: height.h, fit: BoxFit.fill),
SizedBox(height: 16.h), SizedBox(height: 16.h),
(noDataText ?? LocaleKeys.noDataAvailable.tr()) (noDataText ?? LocaleKeys.noDataAvailable.tr()).toText14(isBold: true, color: AppColors.greyTextColor, isCenter: true).paddingSymmetrical(64.w, 0.h),
.toText14(isBold: true, color: AppColors.greyTextColor, isCenter: true)
.paddingSymmetrical(64.w, 0.h),
SizedBox(height: 16.h), SizedBox(height: 16.h),
callToActionButton callToActionButton
], ],
@ -373,8 +370,7 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Lottie.asset(AppAnimations.loadingAnimation, Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill),
repeat: true, reverse: false, frameRate: FrameRate(60), width: 80.h, height: 80.h, fit: BoxFit.fill),
SizedBox(height: 8.h), SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true, isBold: true), (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor, isCenter: true, isBold: true),
SizedBox(height: 8.h), SizedBox(height: 8.h),
@ -400,8 +396,7 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Lottie.asset(AppAnimations.errorAnimation, Lottie.asset(AppAnimations.errorAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h), SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor).paddingSymmetrical(24.h, 0.h), (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 8.h), SizedBox(height: 8.h),
@ -502,8 +497,7 @@ class Utils {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Lottie.asset(AppAnimations.warningAnimation, Lottie.asset(AppAnimations.warningAnimation, repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
repeat: false, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill),
SizedBox(height: 8.h), SizedBox(height: 8.h),
(loadingText ?? LocaleKeys.loadingText.tr()).toText15(color: AppColors.blackColor, letterSpacing: 0), (loadingText ?? LocaleKeys.loadingText.tr()).toText15(color: AppColors.blackColor, letterSpacing: 0),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -623,10 +617,8 @@ class Utils {
} }
// Replace HTML line breaks with newlines // Replace HTML line breaks with newlines
var withLineBreaks = htmlString var withLineBreaks =
.replaceAll(RegExp(r'<br\s*\/?>', multiLine: true), '\n') htmlString.replaceAll(RegExp(r'<br\s*\/?>', multiLine: true), '\n').replaceAll(RegExp(r'<\/p>', multiLine: true), '\n').replaceAll(RegExp(r'<divider>', multiLine: true), '\n');
.replaceAll(RegExp(r'<\/p>', multiLine: true), '\n')
.replaceAll(RegExp(r'<divider>', multiLine: true), '\n');
// Remove all other HTML tags // Remove all other HTML tags
var withoutTags = withLineBreaks.replaceAll(RegExp(r'<[^>]*>'), ''); var withoutTags = withLineBreaks.replaceAll(RegExp(r'<[^>]*>'), '');
@ -704,20 +696,7 @@ class Utils {
final year = parts[0]; final year = parts[0];
// Map month number to short month name (Hijri months) // Map month number to short month name (Hijri months)
const hijriMonthNames = [ const hijriMonthNames = ['Muharram', 'Safar', 'Rabi I', 'Rabi II', 'Jumada I', 'Jumada II', 'Rajab', 'Sha\'ban', 'Ramadan', 'Shawwal', 'Dhu al-Qi\'dah', 'Dhu al-Hijjah'];
'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 monthIndex = int.tryParse(parts[1]) ?? 1;
final month = hijriMonthNames[monthIndex - 1]; final month = hijriMonthNames[monthIndex - 1];
@ -774,21 +753,23 @@ class Utils {
String? errorAsset, String? errorAsset,
}) { }) {
final Color? resolvedColor = iconColor ?? (applyThemeColor && AppColors.isDarkMode ? AppColors.textColor : null); final Color? resolvedColor = iconColor ?? (applyThemeColor && AppColors.isDarkMode ? AppColors.textColor : null);
return SvgPicture.asset( return PreserveSvgColors(
icon, child: SvgPicture.asset(
colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withOpacity(0.5) : resolvedColor, BlendMode.srcIn) : null, icon,
width: width, colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withValues(alpha: 0.5) : resolvedColor, BlendMode.srcIn) : null,
height: height, width: width,
fit: fit, height: height,
errorBuilder: errorAsset != null fit: fit,
? (context, error, stackTrace) => SvgPicture.asset( errorBuilder: errorAsset != null
errorAsset, ? (context, error, stackTrace) => SvgPicture.asset(
colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withOpacity(0.5) : resolvedColor, BlendMode.srcIn) : null, errorAsset,
width: width, colorFilter: resolvedColor != null ? ColorFilter.mode(isDisabled ? resolvedColor.withValues(alpha: 0.5) : resolvedColor, BlendMode.srcIn) : null,
height: height, width: width,
fit: fit, height: height,
) fit: fit,
: null, )
: null,
),
); );
} }
@ -802,14 +783,16 @@ class Utils {
}) { }) {
final iconH = height ?? 24.h; final iconH = height ?? 24.h;
final iconW = width ?? 24.w; final iconW = width ?? 24.w;
return SvgPicture.network( return PreserveSvgColors(
url, child: SvgPicture.network(
colorFilter: ColorFilter.mode( url,
isDisabled ? iconColor.withOpacity(0.5) : iconColor, colorFilter: ColorFilter.mode(
BlendMode.srcIn, 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 iconH = height ?? 24.h;
final iconW = width ?? 24.w; final iconW = width ?? 24.w;
return Container( return PreserveImageColors(
decoration: BoxDecoration( child: Container(
border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, decoration: BoxDecoration(
borderRadius: border != null ? BorderRadius.circular(borderRadius ?? 12.r) : null, 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({ static Widget buildImgWithAssetsWithBorderRadius({
@ -847,21 +831,23 @@ class Utils {
}) { }) {
final iconH = height ?? 24.h; final iconH = height ?? 24.h;
final iconW = width ?? 24.w; final iconW = width ?? 24.w;
return Container( return PreserveImageColors(
width: iconW, child: Container(
height: iconH, width: iconW,
decoration: BoxDecoration( height: iconH,
border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null, decoration: BoxDecoration(
borderRadius: borderRadius != null ? BorderRadius.circular(borderRadius ?? 12.r) : null, border: border != null ? Border.all(color: AppColors.whiteColor, width: border) : null,
image: DecorationImage( borderRadius: BorderRadius.circular(borderRadius ?? 12.r),
image: AssetImage( image: DecorationImage(
icon, image: AssetImage(
), icon,
fit: fit)), ),
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({ static Widget buildImgWithNetwork({
required String url, required String url,
required Color iconColor, required Color iconColor,
@ -873,16 +859,18 @@ class Utils {
}) { }) {
final iconH = height ?? 24.h; final iconH = height ?? 24.h;
final iconW = width ?? 24.w; final iconW = width ?? 24.w;
return Image.network( return PreserveImageColors(
url, child: Image.network(
width: iconW, url,
height: iconH, width: iconW,
fit: fit, height: iconH,
errorBuilder: errorBuilder ?? 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); //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, spacing: spacing.w,
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
Image.asset(AppAssets.mada, width: width.h, height: height.h), PreserveImageColors(
Image.asset( child: Image.asset(AppAssets.mada, width: width.h, height: height.h),
AppAssets.tamaraEng, ),
width: width.h, PreserveImageColors(
height: height.h, child: Image.asset(
fit: BoxFit.contain, AppAssets.tamaraEng,
errorBuilder: (context, error, stackTrace) { width: width.h,
debugPrint('Failed to load Tamara PNG in payment methods: $error'); height: height.h,
return Utils.buildSvgWithAssets(icon: AppAssets.tamara, width: 35.h, height: 35.h, fit: BoxFit.contain); 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), PreserveImageColors(
Image.asset(AppAssets.mastercard, width: width.h, height: height.h), child: Image.asset(AppAssets.mastercard, width: width.h, height: height.h),
Platform.isIOS ? Image.asset(AppAssets.applePay, width: width.h, height: height.h) : SizedBox.shrink(), ),
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, static Widget getPaymentAmountWithSymbol(Widget paymentAmountWidget, Color iconColor, double iconSize, {bool isSaudiCurrency = true, bool isExpanded = true}) {
{bool isSaudiCurrency = true, bool isExpanded = true}) {
return Row( return Row(
mainAxisAlignment: isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start, mainAxisAlignment: isExpanded ? MainAxisAlignment.spaceBetween : MainAxisAlignment.start,
children: [ children: [
@ -965,13 +964,7 @@ class Utils {
// text: NumberFormat.currency(locale: 'en_US', symbol: " ", decimalDigits: 0).format(habibWalletAmount), // text: NumberFormat.currency(locale: 'en_US', symbol: " ", decimalDigits: 0).format(habibWalletAmount),
// text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount)).toStringAsFixed(2)}", // text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount)).toStringAsFixed(2)}",
text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount))}", text: " ${(NumberFormat.decimalPattern().format(habibWalletAmount))}",
style: TextStyle( style: TextStyle(color: resolvedTextColor, fontSize: fontSize ?? 32.f, letterSpacing: letterSpacing ?? -4, fontFamily: "Poppins", fontWeight: fontWeight ?? FontWeight.w600, height: 1),
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) { static PatientDoctorAppointmentList? convertToPatientDoctorAppointmentList(HospitalsModel? hospital) {
if (hospital == null) return null; if (hospital == null) return null;
return PatientDoctorAppointmentList( return PatientDoctorAppointmentList(
filterName: hospital.name, filterName: hospital.name, distanceInKMs: hospital.distanceInKilometers?.toString(), projectTopName: hospital.name, projectBottomName: hospital.name, model: hospital, isHMC: hospital.isHMC);
distanceInKMs: hospital.distanceInKilometers?.toString(),
projectTopName: hospital.name,
projectBottomName: hospital.name,
model: hospital,
isHMC: hospital.isHMC);
} }
static HospitalsModel? convertToHospitalsModel(PatientDoctorAppointmentList? item) { static HospitalsModel? convertToHospitalsModel(PatientDoctorAppointmentList? item) {

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart';
class LaserBodyParts { class LaserBodyParts {
static final LaserBodyParts _instance = LaserBodyParts._internal(); static final LaserBodyParts _instance = LaserBodyParts._internal();
@ -9,11 +9,11 @@ class LaserBodyParts {
factory LaserBodyParts() => _instance; factory LaserBodyParts() => _instance;
static Widget image(String assetUri) { 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) { 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<String, Widget> maleBodyMap = { Map<String, Widget> maleBodyMap = {

@ -16,6 +16,8 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
class ProfileSettingsViewModel extends ChangeNotifier { class ProfileSettingsViewModel extends ChangeNotifier {
static const String _darkModeKey = 'is_dark_mode'; 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 CacheService _cacheService;
final ProfileSettingsRepo profileSettingsRepo; final ProfileSettingsRepo profileSettingsRepo;
@ -25,6 +27,13 @@ class ProfileSettingsViewModel extends ChangeNotifier {
bool _isDarkMode = false; bool _isDarkMode = false;
bool get isDarkMode => _isDarkMode; 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 // Update email state
bool isUpdateEmailLoading = false; bool isUpdateEmailLoading = false;
bool isUpdateEmailSuccess = false; bool isUpdateEmailSuccess = false;
@ -163,6 +172,47 @@ class ProfileSettingsViewModel extends ChangeNotifier {
notifyListeners(); 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 // Update patient info
Future<void> updatePatientInfo({ Future<void> updatePatientInfo({

@ -687,6 +687,15 @@ abstract class LocaleKeys {
static const offTheme = 'offTheme'; static const offTheme = 'offTheme';
static const dimTheme = 'dimTheme'; static const dimTheme = 'dimTheme';
static const bwTheme = 'bwTheme'; 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 permissions = 'permissions';
static const cameraPermission = 'cameraPermission'; static const cameraPermission = 'cameraPermission';
static const locationPermission = 'locationPermission'; static const locationPermission = 'locationPermission';
@ -1578,7 +1587,6 @@ abstract class LocaleKeys {
static const waterConsumption = 'waterConsumption'; static const waterConsumption = 'waterConsumption';
static const selectNumberOfReminders = 'selectNumberOfReminders'; static const selectNumberOfReminders = 'selectNumberOfReminders';
static const h2oSettings = 'h2oSettings'; static const h2oSettings = 'h2oSettings';
static const settingsSavedSuccessfully = 'settingsSavedSuccessfully';
static const yourName = 'yourName'; static const yourName = 'yourName';
static const ageYears = 'ageYears'; static const ageYears = 'ageYears';
static const numberOfRemindersInADay = 'numberOfRemindersInADay'; static const numberOfRemindersInADay = 'numberOfRemindersInADay';

@ -134,6 +134,9 @@ Future<void> callInitializations() async {
// Restore persisted dark-mode preference before the first frame. // Restore persisted dark-mode preference before the first frame.
getIt.get<ProfileSettingsViewModel>().loadDarkMode(); getIt.get<ProfileSettingsViewModel>().loadDarkMode();
// Restore persisted accessibility settings before the first frame.
getIt.get<ProfileSettingsViewModel>().loadAccessibilitySettings();
} }
void main() async { void main() async {
@ -282,6 +285,55 @@ void main() async {
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({super.key}); 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(<double>[
-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(<double>[
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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
@ -300,13 +352,16 @@ class MyApp extends StatelessWidget {
// title: 'Dr. AlHabib', // title: 'Dr. AlHabib',
title: 'Dr. AlHabib Beta', title: 'Dr. AlHabib Beta',
builder: (context, mchild) { builder: (context, mchild) {
return MediaQuery( Widget child = MediaQuery(
data: MediaQuery.of(context).copyWith( data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.noScaling, textScaler: TextScaler.linear(profileVm.getFontScaleMultiplier()),
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
), ),
child: mchild!, child: mchild!,
); );
// Apply visual mode filters app-wide
return _applyVisualMode(profileVm.visualMode, child);
}, },
showSemanticsDebugger: false, showSemanticsDebugger: false,
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,

@ -570,8 +570,9 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
// Todo: what is this???? Api Key??? 😲 // Todo: what is this???? Api Key??? 😲
child: Image.network( child: Utils.buildImgWithNetwork(
"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}", 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, fit: BoxFit.cover,
width: double.infinity, width: double.infinity,
), ),

@ -112,7 +112,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
@ -230,9 +230,9 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
children: [ children: [
Row( Row(
children: [ 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), 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), ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h), SizedBox(height: 16.h),
@ -352,7 +352,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), .toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading),
SizedBox(height: 16.h), SizedBox(height: 16.h),
LocaleKeys.tamara.tr(context: context) LocaleKeys.tamara.tr(context: context)

@ -139,8 +139,9 @@ class _MyDoctorsPageState extends State<MyDoctorsPage> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -261,8 +262,9 @@ class _MyDoctorsPageState extends State<MyDoctorsPage> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
(doctor?.doctorImageURL ?? doctor?.doctorImage ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"), url: (doctor?.doctorImageURL ?? doctor?.doctorImage ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png"),
iconColor: AppColors.transparent,
width: 24.w, width: 24.w,
height: 24.h, height: 24.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -227,10 +227,11 @@ class _AppointmentCardState extends State<AppointmentCard> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
widget.isLoading url: widget.isLoading
? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png' ? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png'
: widget.patientAppointmentHistoryResponseModel.doctorImageURL!, : widget.patientAppointmentHistoryResponseModel.doctorImageURL!,
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -280,8 +281,9 @@ class _AppointmentCardState extends State<AppointmentCard> {
SizedBox(width: 12.w), SizedBox(width: 12.w),
(widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null && (widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null &&
widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty) widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL!.isNotEmpty)
? Image.network( ? Utils.buildImgWithNetwork(
widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png", url: widget.patientAppointmentHistoryResponseModel.doctorNationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SAU.png",
iconColor: AppColors.transparent,
width: 20.h, width: 20.h,
height: 15.h, height: 15.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -58,8 +58,9 @@ class AppointmentDoctorCard extends StatelessWidget {
children: [ children: [
Column( Column(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
patientAppointmentHistoryResponseModel.doctorImageURL!, url: patientAppointmentHistoryResponseModel.doctorImageURL!,
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -10,6 +10,9 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import '../../../core/utils/utils.dart';
import '../../../theme/colors.dart';
class BuildDoctorRowAppointmentRating extends StatelessWidget { class BuildDoctorRowAppointmentRating extends StatelessWidget {
final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; final PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel;
@ -22,8 +25,9 @@ class BuildDoctorRowAppointmentRating extends StatelessWidget {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
patientAppointmentHistoryResponseModel.doctorImageURL!, url: patientAppointmentHistoryResponseModel.doctorImageURL!,
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -86,8 +86,9 @@ class _DoctorResponsePageState extends State<DoctorResponsePage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png",
iconColor: AppColors.blackBgColor,
width: 50.w, width: 50.w,
height: 50.h, height: 50.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -87,8 +87,9 @@ class AskDoctorAppointmentCard extends StatelessWidget {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
askDoctorAppointmentHistoryList.doctorImageURL!, url: askDoctorAppointmentHistoryList.doctorImageURL!,
iconColor: AppColors.blackBgColor,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -28,8 +28,9 @@ class DoctorResponseTransactions extends StatelessWidget {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png",
iconColor: AppColors.blackBgColor,
width: 50.w, width: 50.w,
height: 50.h, height: 50.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -85,7 +86,11 @@ class DoctorResponseTransactions extends StatelessWidget {
SizedBox(height: 16.h), SizedBox(height: 16.h),
Row( Row(
children: [ children: [
Image.asset(getIt.get<AppState>().getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, width: 42.w, height: 42.h), Utils.buildImgWithAssets(
icon: getIt.get<AppState>().getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
width: 42.w,
height: 42.h,
),
SizedBox(width: 8.w), SizedBox(width: 8.w),
Container( Container(
padding: EdgeInsets.all(8.w), padding: EdgeInsets.all(8.w),
@ -116,10 +121,16 @@ class DoctorResponseTransactions extends StatelessWidget {
borderRadius: BorderRadius.circular(8.r), borderRadius: BorderRadius.circular(8.r),
), ),
child: doctorResponseModel.transactions![index].doctorResponse!.toText12(color: AppColors.textColor, isBold: true), child: doctorResponseModel.transactions![index].doctorResponse!.toText12(color: AppColors.textColor, isBold: true),
), ),
SizedBox(width: 8.w), 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), 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),
],
), ),
], ],
); );

@ -132,8 +132,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 64.h, width: 64.h,
height: 64.h, height: 64.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -191,8 +192,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, url: myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!,
iconColor: AppColors.transparent,
width: 64.h, width: 64.h,
height: 64.h, height: 64.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -383,8 +385,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
myAppointmentsVM.patientFavouriteDoctorsList[index].doctorImageUrl!, url: myAppointmentsVM.patientFavouriteDoctorsList[index].doctorImageUrl!,
iconColor: AppColors.transparent,
width: 64.h, width: 64.h,
height: 64.h, height: 64.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -83,8 +83,9 @@ class DoctorProfilePage extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL!, url: bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL!,
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -109,8 +110,9 @@ class DoctorProfilePage extends StatelessWidget {
), ),
], ],
), ),
Image.network( Utils.buildImgWithNetwork(
bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL!, url: bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL!,
iconColor: AppColors.transparent,
width: 32.h, width: 32.h,
height: 32.h, height: 32.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -66,8 +66,8 @@ class ImmediateLiveCarePaymentDetails extends StatelessWidget {
padding: EdgeInsets.all(16.h), padding: EdgeInsets.all(16.h),
child: Row( child: Row(
children: [ children: [
Image.asset( Utils.buildImgWithAssets(
appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, icon: appState.getAuthenticatedUser()?.gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
width: 52.h, width: 52.h,
height: 52.h, height: 52.h,
), ),

@ -106,7 +106,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true), LocaleKeys.mada.tr(context: context).toText16(isBold: true),
], ],
@ -187,9 +187,9 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
children: [ children: [
Row( Row(
children: [ 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), 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), SizedBox(height: 16.h),
@ -271,7 +271,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.tamara.tr(context: context).toText16(isBold: true), LocaleKeys.tamara.tr(context: context).toText16(isBold: true),
], ],

@ -84,9 +84,10 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
children: [ children: [
Row( Row(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
bookAppointmentsViewModel.selectedDoctor.doctorImageURL ?? url: bookAppointmentsViewModel.selectedDoctor.doctorImageURL ??
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png",
iconColor: AppColors.transparent,
width: 50.h, width: 50.h,
height: 50.h, height: 50.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -104,9 +105,10 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
SizedBox(width: 12.w), SizedBox(width: 12.w),
(bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL != null && (bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL != null &&
bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!.isNotEmpty) bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL!.isNotEmpty)
? Image.network( ? Utils.buildImgWithNetwork(
bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ?? url: bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL ??
"https://hmgwebservices.com/Images/flag/SAU.png", "https://hmgwebservices.com/Images/flag/SAU.png",
iconColor: AppColors.transparent,
width: 20.h, width: 20.h,
height: 15.h, height: 15.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -334,12 +336,10 @@ class _ReviewAppointmentPageState extends State<ReviewAppointmentPage> {
// Show default image (no image data or user has no uploaded image) // Show default image (no image data or user has no uploaded image)
print('📷 Showing default avatar for user $currentPatientId'); print('📷 Showing default avatar for user $currentPatientId');
return Image.asset( return Utils.buildImgWithAssets(
defaultImage, icon: defaultImage,
key: ValueKey('default_$currentPatientId'),
width: 52.w, width: 52.w,
height: 52.h, height: 52.h,
gaplessPlayback: true,
); );
} }

@ -116,7 +116,7 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true), LocaleKeys.mada.tr(context: context).toText16(isBold: true),
], ],
@ -197,9 +197,9 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
children: [ children: [
Row( Row(
children: [ 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), 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), SizedBox(height: 16.h),
@ -281,7 +281,7 @@ class _WaitingAppointmentPaymentPageState extends State<WaitingAppointmentPaymen
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.tamara.tr(context: context).toText16(isBold: true), LocaleKeys.tamara.tr(context: context).toText16(isBold: true),
], ],

@ -52,10 +52,11 @@ class DoctorCard extends StatelessWidget {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
isLoading url: isLoading
? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png" ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png"
: doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png", : doctorsListResponseModel.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png",
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -120,15 +121,16 @@ class DoctorCard extends StatelessWidget {
.toString() .toString()
.toText12(isBold: true, color: AppColors.textColor, maxLine: 2, isEnglishOnly: true) .toText12(isBold: true, color: AppColors.textColor, maxLine: 2, isEnglishOnly: true)
.toShimmer2(isShow: isLoading), .toShimmer2(isShow: isLoading),
), ),
SizedBox(width: 6.w), SizedBox(width: 6.w),
Image.network( Utils.buildImgWithNetwork(
isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png", url: isLoading ? "https://hmgwebservices.com/Images/flag/SYR.png" : doctorsListResponseModel.nationalityFlagURL ?? "https://hmgwebservices.com/Images/flag/SYR.png",
width: 20.h, iconColor: AppColors.transparent,
height: 15.h, width: 20.h,
fit: BoxFit.fill, height: 15.h,
).toShimmer2(isShow: isLoading), fit: BoxFit.fill,
], ).toShimmer2(isShow: isLoading),
],
), ),
SizedBox(height: 6.h), SizedBox(height: 6.h),
Wrap( Wrap(

@ -301,7 +301,7 @@ class _MyChildVaccinePageState extends State<MyChildVaccinePage> {
), ),
child: Row( child: Row(
children: [ 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), SizedBox(width: 8.w),
Text( Text(
"${children[index]['name']} (${children[index]['gender']})", "${children[index]['name']} (${children[index]['gender']})",

@ -282,8 +282,8 @@ class _ComprehensiveCheckupPageState extends State<ComprehensiveCheckupPage> {
final String imagePath = appStateLocal.isArabic() ? AppAssets.comprehensiveCheckupAr : AppAssets.comprehensiveCheckupEn; final String imagePath = appStateLocal.isArabic() ? AppAssets.comprehensiveCheckupAr : AppAssets.comprehensiveCheckupEn;
return Stack( return Stack(
children: [ children: [
Image.asset( Utils.buildImgWithAssets(
imagePath, icon: imagePath,
width: double.infinity, width: double.infinity,
fit: BoxFit.cover, fit: BoxFit.cover,
).paddingAll(16.w), ).paddingAll(16.w),

@ -259,8 +259,9 @@ class FeedbackPage extends StatelessWidget {
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
contactUsViewModel.patientFeedbackSelectedAppointment!.doctorImageURL!, url: contactUsViewModel.patientFeedbackSelectedAppointment!.doctorImageURL!,
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -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:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../../core/utils/utils.dart';
class FindUsPage extends StatelessWidget { class FindUsPage extends StatelessWidget {
FindUsPage({super.key}); FindUsPage({super.key});
@ -120,8 +122,9 @@ class FindUsPage extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -70,8 +70,9 @@ class LiveChatPage extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -78,8 +78,9 @@ class FindUsItemCard extends StatelessWidget {
: SizedBox.shrink(), : SizedBox.shrink(),
Row( Row(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
getHMGLocationsModel.projectImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", url: getHMGLocationsModel.projectImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 40.h, width: 40.h,
height: 40.h, height: 40.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -211,7 +211,7 @@ class _CovidPaymentScreenState extends State<CovidPaymentScreen> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true), LocaleKeys.mada.tr(context: context).toText16(isBold: true),
], ],
@ -252,9 +252,9 @@ class _CovidPaymentScreenState extends State<CovidPaymentScreen> {
children: [ children: [
Row( Row(
children: [ 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), 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), SizedBox(height: 16.h),
@ -296,21 +296,11 @@ class _CovidPaymentScreenState extends State<CovidPaymentScreen> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.asset( Utils.buildImgWithAssets(
AppAssets.tamaraEng, icon: AppAssets.tamaraEng,
width: 72.h, width: 72.h,
height: 25.h, height: 25.h,
fit: BoxFit.contain, 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), SizedBox(height: 16.h),
LocaleKeys.tamara.tr(context: context).toText16(isBold: true), LocaleKeys.tamara.tr(context: context).toText16(isBold: true),

@ -102,7 +102,7 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true), LocaleKeys.mada.tr(context: context).toText16(isBold: true),
], ],
@ -188,9 +188,9 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
children: [ children: [
Row( Row(
children: [ 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), 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), SizedBox(height: 16.h),
@ -277,7 +277,7 @@ class _ErOnlineCheckinPaymentPageState extends State<ErOnlineCheckinPaymentPage>
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.tamara.tr(context: context).toText16(isBold: true), LocaleKeys.tamara.tr(context: context).toText16(isBold: true),
], ],

@ -85,7 +85,7 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: false), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: false),
], ],
@ -183,9 +183,9 @@ class _WalletPaymentConfirmPageState extends State<WalletPaymentConfirmPage> {
children: [ children: [
Row( Row(
children: [ 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), 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), ).toShimmer2(isShow: false),
SizedBox(height: 16.h), SizedBox(height: 16.h),

@ -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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart'; import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import '../../core/utils/utils.dart';
class ServiceInfoPage extends StatelessWidget { class ServiceInfoPage extends StatelessWidget {
final String serviceName; final String serviceName;
final String serviceHeader; final String serviceHeader;
@ -37,8 +39,8 @@ class ServiceInfoPage extends StatelessWidget {
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
child: Transform.flip( child: Transform.flip(
flipX: getIt.get<AppState>().isArabic(), flipX: getIt.get<AppState>().isArabic(),
child: Image.asset( child: Utils.buildImgWithAssets(
serviceImage, icon: serviceImage,
fit: BoxFit.fitHeight, fit: BoxFit.fitHeight,
// height: 480.h, // height: 480.h,
// width: 520.w, // width: 520.w,

@ -56,8 +56,8 @@ class LargeServiceCard extends StatelessWidget {
topLeft: Radius.circular(24.r), topLeft: Radius.circular(24.r),
topRight: Radius.circular(24.r), topRight: Radius.circular(24.r),
), ),
child: Image.asset( child: Utils.buildImgWithAssets(
serviceCardData.largeCardIcon, icon: serviceCardData.largeCardIcon,
fit: BoxFit.cover, fit: BoxFit.cover,
width: double.infinity, width: double.infinity,
height: isFoldable ? 190.h : (isTablet ? 200.h : 180.h), height: isFoldable ? 190.h : (isTablet ? 200.h : 180.h),
@ -72,7 +72,7 @@ class LargeServiceCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
isPNG isPNG
? Image.asset(serviceCardData.icon, width: 35.h, height: 35.h) ? Utils.buildImgWithAssets(icon: serviceCardData.icon, width: 35.h, height: 35.h)
: Container( : Container(
height: 48.h, height: 48.h,
width: 48.h, width: 48.h,
@ -204,7 +204,7 @@ class FadedLargeServiceCard extends StatelessWidget {
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
child: Transform.flip( child: Transform.flip(
flipX: getIt.get<AppState>().isArabic(), flipX: getIt.get<AppState>().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( Positioned(
top: 0, top: 0,
@ -234,7 +234,7 @@ class FadedLargeServiceCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
isPNG 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( : Container(
height: 32.h, height: 32.h,
width: 32.h, width: 32.h,

@ -63,7 +63,7 @@ class WelcomeWidget extends StatelessWidget {
), ),
imageWidget ?? imageWidget ??
(imageUrl != null (imageUrl != null
? Image.asset(imageUrl!, width: 40, height: 40) ? Utils.buildImgWithAssets(icon: imageUrl!, width: 40, height: 40)
: SizedBox(width: 40, height: 40)), : SizedBox(width: 40, height: 40)),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

@ -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/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
@ -82,8 +83,9 @@ class InsuranceApprovalDetailsPage extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
insuranceApprovalResponseModel.doctorImageURL!, url: insuranceApprovalResponseModel.doctorImageURL!,
iconColor: AppColors.blackBgColor,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -106,8 +106,9 @@ class InsuranceApprovalCard extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
isLoading ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png" : insuranceApprovalResponseModel.doctorImageURL!, url: isLoading ? "https://hmgwebservices.com/Images/MobileImages/OALAY/1439.png" : insuranceApprovalResponseModel.doctorImageURL!,
iconColor: AppColors.blackBgColor,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -55,8 +55,9 @@ class LabResultItemView extends StatelessWidget {
SizedBox(height: 8.h), SizedBox(height: 8.h),
Row( Row(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
isLoading ? "" : labOrder!.doctorImageURL!, url: isLoading ? "" : labOrder!.doctorImageURL!,
iconColor: AppColors.transparent,
width: 24.h, width: 24.h,
height: 24.h, height: 24.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -1,9 +1,9 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/app_assets.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/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/string_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.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'; 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( Container(
padding: EdgeInsets.all(8.r), padding: EdgeInsets.all(8.r),
decoration: BoxDecoration(gradient: AppColors.aiLinearGradient, borderRadius: BorderRadius.circular(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,
),
), ),
], ],
), ),

@ -908,8 +908,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
child: FadeInAnimation( child: FadeInAnimation(
child: Row( child: Row(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!, url: prescriptionVM.patientPrescriptionOrders[index].doctorImageURL!,
iconColor: AppColors.transparent,
width: 40.h, width: 40.h,
height: 40.h, height: 40.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -1059,8 +1060,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
? Column( ? Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 64.h, width: 64.h,
height: 64.h, height: 64.h,
fit: BoxFit.cover, fit: BoxFit.cover,
@ -1106,8 +1108,9 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!, url: myAppointmentsVM.patientMyDoctorsList[index].doctorImageURL!,
iconColor: AppColors.transparent,
width: 64.h, width: 64.h,
height: 64.h, height: 64.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -139,8 +139,9 @@ class _PatientSickleavesListPageState extends State<PatientSickleavesListPage> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
model.patientSickLeaveList[index].doctorImageURL!, url: model.patientSickLeaveList[index].doctorImageURL!,
iconColor: AppColors.transparent,
width: 24.h, width: 24.h,
height: 24.h, height: 24.h,
fit: BoxFit.fill, fit: BoxFit.fill,

@ -83,8 +83,9 @@ class _VaccineListPageState extends State<VaccineListPage> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -163,9 +163,10 @@ class _MedicalFileAppointmentCardState extends State<MedicalFileAppointmentCard>
children: [ children: [
Row( Row(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
widget.patientAppointmentHistoryResponseModel.doctorImageURL ?? url: widget.patientAppointmentHistoryResponseModel.doctorImageURL ??
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 30.h, width: 30.h,
height: 30.h, height: 30.h,
fit: BoxFit.fill, fit: BoxFit.fill,

@ -56,8 +56,9 @@ class PatientSickLeaveCard extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientSickLeavesResponseModel.doctorImageURL!, url: isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientSickLeavesResponseModel.doctorImageURL!,
iconColor: AppColors.transparent,
width: 30.h, width: 30.h,
height: 30.h, height: 30.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -241,8 +241,9 @@ class _MedicalReportsPageState extends State<MedicalReportsPage> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
report.doctorImageURL!, url: report.doctorImageURL!,
iconColor: AppColors.transparent,
width: 24.h, width: 24.h,
height: 24.h, height: 24.h,
fit: BoxFit.fill, fit: BoxFit.fill,

@ -42,8 +42,9 @@ class PatientMedicalReportCard extends StatelessWidget {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientMedicalReportResponseModel.doctorImageURL!, url: isLoading ? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png" : patientMedicalReportResponseModel.doctorImageURL!,
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.fill, fit: BoxFit.fill,

@ -149,8 +149,9 @@ class _MyInvoicesDetailsPageState extends State<MyInvoicesDetailsPage> {
children: [ children: [
Column( Column(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
widget.getInvoiceDetailsResponseModel.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', url: widget.getInvoiceDetailsResponseModel.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png',
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -102,8 +102,9 @@ class InvoiceListCard extends StatelessWidget {
children: [ children: [
Column( Column(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
getInvoicesListResponseModel.doctorImageUrl ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', url: getInvoicesListResponseModel.doctorImageUrl ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png',
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -71,8 +71,9 @@ class PharmacyInvoiceCard extends StatelessWidget {
children: [ children: [
Column( Column(
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
pharmacyInvoice.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png', url: pharmacyInvoice.doctorImageURL ?? 'https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png',
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -84,24 +84,9 @@ class _OfferAndDiscountsFullScreenSwiperPageState extends State<OfferAndDiscount
flipX: getIt.get<AppState>().isArabic(), flipX: getIt.get<AppState>().isArabic(),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16.r), borderRadius: BorderRadius.circular(16.r),
child: Image.asset( child: Utils.buildImgWithAssets(
_promoImages[index], icon: _promoImages[index],
fit: BoxFit.contain, 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),
),
),
);
},
), ),
), ),
), ),

@ -2,6 +2,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.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/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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_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'; 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: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular(24.h), borderRadius: BorderRadius.circular(24.h),
child: Image.network( child: Utils.buildImgWithNetwork(
offer.imageUrl!, url: offer.imageUrl!,
iconColor: AppColors.blackBgColor,
width: double.infinity, width: double.infinity,
height: 250.h, height: 250.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -158,8 +158,9 @@ class _OffersAndDiscountsPageState extends State<OffersAndDiscountsPage> {
ClipRRect( ClipRRect(
borderRadius: BorderRadius.vertical(top: Radius.circular(24.h)), borderRadius: BorderRadius.vertical(top: Radius.circular(24.h)),
child: SizedBox.expand( child: SizedBox.expand(
child: Image.network( child: Utils.buildImgWithNetwork(
offersAndDiscountVM.filteredOffers[index].imageUrl!, url: offersAndDiscountVM.filteredOffers[index].imageUrl!,
iconColor: AppColors.blackBgColor,
fit: BoxFit.cover, fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
return Container( return Container(

@ -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:hmg_patient_app_new/theme/colors.dart';
import 'package:smooth_corner/smooth_corner.dart'; import 'package:smooth_corner/smooth_corner.dart';
import '../../../core/utils/utils.dart';
class OffersAndDiscountsCarousel extends StatefulWidget { class OffersAndDiscountsCarousel extends StatefulWidget {
const OffersAndDiscountsCarousel({super.key}); const OffersAndDiscountsCarousel({super.key});
@ -112,19 +114,9 @@ class _OffersAndDiscountsCarouselState extends State<OffersAndDiscountsCarousel>
padding: const EdgeInsets.all(2.0), padding: const EdgeInsets.all(2.0),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(11.r), borderRadius: BorderRadius.circular(11.r),
child: Image.asset( child: Utils.buildImgWithAssets(
imagePath, icon: imagePath,
fit: BoxFit.cover, fit: BoxFit.cover,
errorBuilder: (context, error, stackTrace) {
return Container(
color: AppColors.greyColor,
child: Icon(
Icons.image,
color: AppColors.textColorLight,
size: 32.h,
),
);
},
), ),
), ),
), ),

@ -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_export.dart';
import 'package:hmg_patient_app_new/core/app_state.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/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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
@ -110,8 +111,8 @@ class _ParkingPageState extends State<ParkingPage> {
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
child: Transform.flip( child: Transform.flip(
flipX: getIt.get<AppState>().isArabic(), flipX: getIt.get<AppState>().isArabic(),
child: Image.asset( child: Utils.buildImgWithAssets(
AppAssets.carParkingService, icon: AppAssets.carParkingService,
fit: BoxFit.fitHeight, fit: BoxFit.fitHeight,
height: 480.h, height: 480.h,
width: 520.w, width: 520.w,

@ -61,8 +61,9 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget {
borderRadius: BorderRadius.all( borderRadius: BorderRadius.all(
Radius.circular(5.r), Radius.circular(5.r),
), ),
child: Image.network( child: Utils.buildImgWithNetwork(
prescriptionsViewModel.prescriptionDetailsList[index].imageSRCUrl!, url: prescriptionsViewModel.prescriptionDetailsList[index].imageSRCUrl!,
iconColor: AppColors.blackBgColor,
fit: BoxFit.cover, fit: BoxFit.cover,
width: 60.w, width: 60.w,
height: 70.h, height: 70.h,
@ -99,8 +100,9 @@ class PrescriptionDeliveryOrderSummaryPage extends StatelessWidget {
ClipRRect( ClipRRect(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(20.r), borderRadius: BorderRadius.circular(20.r),
child: Image.network( child: Utils.buildImgWithNetwork(
"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", 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, fit: BoxFit.contain,
), ),
), ),

@ -125,8 +125,9 @@ class _PrescriptionDetailPageState extends State<PrescriptionDetailPage> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
widget.prescriptionsResponseModel.doctorImageURL!, url: widget.prescriptionsResponseModel.doctorImageURL!,
iconColor: AppColors.blackBgColor,
width: 24.h, width: 24.h,
height: 24.h, height: 24.h,
fit: BoxFit.fill, fit: BoxFit.fill,

@ -42,8 +42,9 @@ class PrescriptionItemView extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
spacing: 8.h, spacing: 8.h,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].imageThumbUrl!, url: isLoading ? "" : prescriptionVM.prescriptionDetailsList[index].imageThumbUrl!,
iconColor: AppColors.blackBgColor,
width: 60.h, width: 60.h,
height: 60.h, height: 60.h,
errorBuilder: (cxt, child, tr) { errorBuilder: (cxt, child, tr) {

@ -150,8 +150,9 @@ class _PrescriptionsListPageState extends State<PrescriptionsListPage> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
model.patientPrescriptionOrders[index].doctorImageURL!, url: model.patientPrescriptionOrders[index].doctorImageURL!,
iconColor: AppColors.blackBgColor,
width: 24.h, width: 24.h,
height: 24.h, height: 24.h,
fit: BoxFit.fill, fit: BoxFit.fill,

@ -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<AccessibilityPage> createState() => _AccessibilityPageState();
}
class _AccessibilityPageState extends State<AccessibilityPage> {
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<ProfileSettingsViewModel>(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<ProfileSettingsViewModel>(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(<double>[
-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(<double>[
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;
}
}
}

@ -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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.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/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/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_email_widget.dart';
import 'package:hmg_patient_app_new/presentation/profile_settings/widgets/update_emergency_contact_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<ProfileSettings> {
); );
}, },
), ),
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<FamilyCardWidget> {
], ],
).expanded, ).expanded,
// Icon(Icons.qr_code, size: 56.h) // 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), SizedBox(height: 4.h),

@ -128,12 +128,10 @@ class _ProfilePictureWidgetState extends State<ProfilePictureWidget> {
// Show default image (no image data or user has no uploaded image) // Show default image (no image data or user has no uploaded image)
print('📷 Showing default avatar for user $currentPatientId'); print('📷 Showing default avatar for user $currentPatientId');
return Image.asset( return Utils.buildImgWithAssets(
defaultImage, icon: defaultImage,
key: ValueKey('default_$currentPatientId'),
width: 136.w, width: 136.w,
height: 136.h, height: 136.h,
gaplessPlayback: true,
); );
} }

@ -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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.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/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/accessibility/preserve_svg_colors.dart';
class RadiologyAiAnalysisWidget extends StatelessWidget { class RadiologyAiAnalysisWidget extends StatelessWidget {
final RadiologyAiAnalysisResponse data; final RadiologyAiAnalysisResponse data;
@ -47,7 +48,7 @@ class RadiologyAiAnalysisWidget extends StatelessWidget {
Container( Container(
padding: EdgeInsets.all(8.r), padding: EdgeInsets.all(8.r),
decoration: BoxDecoration(gradient: AppColors.aiLinearGradient, borderRadius: BorderRadius.circular(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)),
), ),
], ],
), ),

@ -401,8 +401,9 @@ class _RadiologyOrdersPageState extends State<RadiologyOrdersPage> {
Row( Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png", url: group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
iconColor: AppColors.transparent,
width: 24.w, width: 24.w,
height: 24.h, height: 24.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -1,7 +1,7 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.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/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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_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'; import 'package:hmg_patient_app_new/features/my_appointments/appointment_rating_view_model.dart';
@ -96,7 +96,13 @@ class _RateAppointmentClinicState extends State<RateAppointmentClinic> {
}); });
}, },
iconSize: 35, 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( : IconButton(
@ -106,7 +112,12 @@ class _RateAppointmentClinicState extends State<RateAppointmentClinic> {
}); });
}, },
iconSize: 35, 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<RateAppointmentClinic> {
); );
} }
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){ ColorFilter getColors(int rating){
switch(rating){ switch(rating){

@ -9,6 +9,9 @@ import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import '../../../core/utils/utils.dart';
import '../../../theme/colors.dart';
class BuildDoctorRow extends StatelessWidget { class BuildDoctorRow extends StatelessWidget {
bool isForClinic = false; bool isForClinic = false;
AppointmentDetails? appointmentDetails; AppointmentDetails? appointmentDetails;
@ -21,8 +24,9 @@ class BuildDoctorRow extends StatelessWidget {
return Padding(padding: EdgeInsets.all(16),child:Row( return Padding(padding: EdgeInsets.all(16),child:Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.network( Utils.buildImgWithNetwork(
isForClinic ? 'https://hmgwebservices.com/Images/Hospitals/${appointmentDetails!.projectID}.jpg' : appointmentDetails!.doctorImageURL , url: isForClinic ? 'https://hmgwebservices.com/Images/Hospitals/${appointmentDetails!.projectID}.jpg' : appointmentDetails!.doctorImageURL ,
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -81,7 +81,7 @@ class SmartwatchHomePage extends StatelessWidget {
), ),
child: Column( child: Column(
children: [ 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), "Apple Watch".needTranslation.toText16(isBold: true),
CustomButton( CustomButton(
text: LocaleKeys.selectSmartWatch.tr(context: context), text: LocaleKeys.selectSmartWatch.tr(context: context),
@ -115,7 +115,7 @@ class SmartwatchHomePage extends StatelessWidget {
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r), decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
child: Column( child: Column(
children: [ 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), .paddingSymmetrical(24.w, 8.h),
"Samsung Watch".needTranslation.toText16(isBold: true), "Samsung Watch".needTranslation.toText16(isBold: true),
CustomButton( CustomButton(
@ -153,7 +153,7 @@ class SmartwatchHomePage extends StatelessWidget {
), ),
child: Column( child: Column(
children: [ 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), "Huawei Watch".needTranslation.toText16(isBold: true),
CustomButton( CustomButton(
text: LocaleKeys.selectSmartWatch.tr(context: context), text: LocaleKeys.selectSmartWatch.tr(context: context),
@ -187,7 +187,7 @@ class SmartwatchHomePage extends StatelessWidget {
), ),
child: Column( child: Column(
children: [ 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), "Whoop Watch".needTranslation.toText16(isBold: true),
CustomButton( CustomButton(
text: LocaleKeys.selectSmartWatch.tr(context: context), text: LocaleKeys.selectSmartWatch.tr(context: context),

@ -48,8 +48,8 @@ class SmartwatchInstructionsPage extends StatelessWidget {
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
spacing: 18.h, spacing: 18.h,
children: [ children: [
Image.asset( Utils.buildImgWithAssets(
smartwatchDetails.watchIcon, icon: smartwatchDetails.watchIcon,
fit: BoxFit.contain, fit: BoxFit.contain,
height: 280.h, height: 280.h,
width: 280.w, width: 280.w,

@ -1,11 +1,11 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:health/health.dart'; import 'package:health/health.dart';
import 'package:fl_chart/fl_chart.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/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/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/string_extensions.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/smartwatches/widgets/health_metric.dart'; import 'package:hmg_patient_app_new/presentation/smartwatches/widgets/health_metric.dart';
@ -53,12 +53,12 @@ class HealthMetricCard extends StatelessWidget {
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
SvgPicture.asset( Utils.buildSvgWithAssets(
metric.svgIcon, icon: metric.svgIcon,
height: 30.h, height: 30.h,
width: 30.h, width: 30.h,
fit: BoxFit.contain, fit: BoxFit.contain,
// color: color, applyThemeColor: false,
), ),
// Icon( // Icon(
// metric.icon, // metric.icon,

@ -2,6 +2,8 @@ import 'dart:ui' as ui;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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/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_dot_widget.dart';
import 'package:hmg_patient_app_new/presentation/symptoms_checker/widgets/organ_tooltip_widget.dart'; import 'package:hmg_patient_app_new/presentation/symptoms_checker/widgets/organ_tooltip_widget.dart';
@ -113,11 +115,21 @@ class _InteractiveBodyWidgetState extends State<InteractiveBodyWidget> {
children: [ children: [
// Body image // Body image
Positioned.fill( Positioned.fill(
child: Image.asset( child: widget.isBodyHidden
widget.bodyImageAsset, ? ColorFiltered(
fit: BoxFit.fill, colorFilter: ColorFilter.mode(
color: widget.isBodyHidden ? Colors.black.withValues(alpha: 0.5) : null, 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 // Organ dots

@ -1779,7 +1779,7 @@ class _CallScreenState extends State<CallScreen> {
children: [ children: [
IconButton( IconButton(
onPressed: onPressAudio, 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, iconSize: circleButtonSize,
tooltip: isMuted.value == true ? "Unmute" : "Mute", tooltip: isMuted.value == true ? "Unmute" : "Mute",
), ),
@ -1791,7 +1791,7 @@ class _CallScreenState extends State<CallScreen> {
IconButton( IconButton(
onPressed: onPressVideo, onPressed: onPressVideo,
iconSize: circleButtonSize, 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( Column(
children: [ children: [

@ -107,7 +107,7 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ 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), SizedBox(height: 16.h),
LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: todoVM.isProcessingPayment), LocaleKeys.mada.tr(context: context).toText16(isBold: true).toShimmer2(isShow: todoVM.isProcessingPayment),
], ],
@ -177,9 +177,9 @@ class _AncillaryOrderPaymentPageState extends State<AncillaryOrderPaymentPage> {
children: [ children: [
Row( Row(
children: [ 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), 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), ).toShimmer2(isShow: todoVM.isProcessingPayment),
SizedBox(height: 16.h), SizedBox(height: 16.h),

@ -224,8 +224,8 @@ class _AncillaryOrderDetailsListState extends State<AncillaryOrderDetailsList> {
Row( Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Image.asset( Utils.buildImgWithAssets(
gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg, icon: gender == 1 ? AppAssets.maleImg : AppAssets.femaleImg,
width: 56.w, width: 56.w,
height: 56.h, height: 56.h,
), ),

@ -138,8 +138,9 @@ class AncillaryOrderCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
if (!isLoading) ...[ if (!isLoading) ...[
Image.network( Utils.buildImgWithNetwork(
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png", url: "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown.png",
iconColor: AppColors.transparent,
width: 63.h, width: 63.h,
height: 63.h, height: 63.h,
fit: BoxFit.cover, fit: BoxFit.cover,

@ -235,12 +235,11 @@ class _VitalSignPageState extends State<VitalSignPage> {
child: Stack( child: Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
Image.asset( Utils.buildImgWithAssets(
getIt.get<AppState>().getAuthenticatedUser()?.gender == 1 icon: getIt.get<AppState>().getAuthenticatedUser()?.gender == 1
? AppAssets.bmiFullBody ? AppAssets.bmiFullBody
: AppAssets.bmiFullBodyFemale, : AppAssets.bmiFullBodyFemale,
fit: BoxFit.cover, fit: BoxFit.cover,
alignment: Alignment.topCenter,
), ),
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; 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/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/theme/colors.dart';
extension WithContainer on Widget { extension WithContainer on Widget {
@ -63,7 +63,7 @@ class DefaultButton extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
if (iconData != null) Icon(iconData, color: textColor), if (iconData != null) Icon(iconData, color: textColor),
if (svgIcon != null) SvgPicture.asset(svgIcon ?? ""), if (svgIcon != null) Utils.buildSvgWithAssets(icon: svgIcon ?? "", applyThemeColor: false),
if (!isTextExpanded) if (!isTextExpanded)
Padding( Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(

@ -78,7 +78,7 @@ class AppCustomChipWidget extends StatelessWidget {
? Chip( ? Chip(
avatar: icon.isNotEmpty avatar: icon.isNotEmpty
? isIconPNG ? isIconPNG
? Image.asset(icon, width: iconS, height: iconS) ? Utils.buildImgWithAssets(icon: icon, width: iconS, height: iconS)
: Utils.buildSvgWithAssets( : Utils.buildSvgWithAssets(
icon: icon, icon: icon,
width: iconS, width: iconS,

@ -79,7 +79,7 @@ class ImageOptions {
} }
}, },
onFilesTap: () async { onFilesTap: () async {
FilePickerResult? result = await FilePicker.platform.pickFiles( FilePickerResult? result = await FilePicker.pickFiles(
type: FileType.custom, type: FileType.custom,
allowedExtensions: [ allowedExtensions: [
'jpg', 'jpg',

@ -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:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:map_launcher/map_launcher.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 /// 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.) /// Can be used in any review screen (HHC, CMC, etc.)
class LocationMapWidget extends StatelessWidget { class LocationMapWidget extends StatelessWidget {
@ -127,24 +129,10 @@ class LocationMapWidget extends StatelessWidget {
ClipRRect( ClipRRect(
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
borderRadius: BorderRadius.circular(24.r), borderRadius: BorderRadius.circular(24.r),
child: Image.network( child: Utils.buildImgWithNetwork(
staticMapUrl, url: staticMapUrl,
iconColor: AppColors.transparent,
fit: BoxFit.contain, 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) { errorBuilder: (context, error, stackTrace) {
return Container( return Container(
height: 165.h, height: 165.h,

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; 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'; import 'package:hmg_patient_app_new/theme/colors.dart';
class FullScreenImageViewer extends StatelessWidget { class FullScreenImageViewer extends StatelessWidget {
@ -22,8 +22,8 @@ class FullScreenImageViewer extends StatelessWidget {
body: Center( body: Center(
child: InteractiveViewer( child: InteractiveViewer(
child: isSvg child: isSvg
? SvgPicture.asset(path, width: double.infinity, fit: BoxFit.contain) ? Utils.buildSvgWithAssets(icon: path, width: double.infinity, fit: BoxFit.contain, applyThemeColor: false)
: Image.asset(width: double.infinity, path, fit: BoxFit.contain), : Utils.buildImgWithAssets(icon: path, width: double.infinity, fit: BoxFit.contain),
), ),
), ),
); );

@ -3,9 +3,9 @@ import 'dart:io';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_nfc_kit/flutter_nfc_kit.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/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.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/buttons/custom_button.dart';
@ -93,9 +93,10 @@ class _NfcLayoutState extends State<NfcLayout> {
SizedBox( SizedBox(
height: 30, height: 30,
), ),
SvgPicture.asset( Utils.buildSvgWithAssets(
"assets/images/nfc/contactless.svg", icon: "assets/images/nfc/contactless.svg",
height: MediaQuery.of(context).size.width / 3, height: MediaQuery.of(context).size.width / 3,
applyThemeColor: false,
), ),
SizedBox( SizedBox(
height: 30, height: 30,
@ -161,8 +162,8 @@ class _NfcLayoutState extends State<NfcLayout> {
SizedBox( SizedBox(
height: 30, height: 30,
), ),
Image.asset( Utils.buildImgWithAssets(
"assets/images/nfc/ic_done.png", icon: "assets/images/nfc/ic_done.png",
height: MediaQuery.of(context).size.width / 3, height: MediaQuery.of(context).size.width / 3,
), ),
SizedBox( SizedBox(

@ -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/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
class UserAvatarWidget extends StatelessWidget { class UserAvatarWidget extends StatelessWidget {
final double width; final double width;
@ -84,11 +85,11 @@ class UserAvatarWidget extends StatelessWidget {
// If decoding fails, show default image // If decoding fails, show default image
return ClipRRect( return ClipRRect(
borderRadius: BorderRadius.circular(effectiveBorderRadius), borderRadius: BorderRadius.circular(effectiveBorderRadius),
child: Image.asset( child: Utils.buildImgWithAssets(
defaultImage, icon: defaultImage,
width: width, width: width,
height: height, height: height,
fit: fit, fit: fit ?? BoxFit.cover,
), ),
); );
} }
@ -97,11 +98,11 @@ class UserAvatarWidget extends StatelessWidget {
// Show default image with rounded corners // Show default image with rounded corners
return ClipRRect( return ClipRRect(
borderRadius: BorderRadius.circular(effectiveBorderRadius), borderRadius: BorderRadius.circular(effectiveBorderRadius),
child: Image.asset( child: Utils.buildImgWithAssets(
defaultImage, icon: defaultImage,
width: width, width: width,
height: height, height: height,
fit: fit, fit: fit ?? BoxFit.cover,
), ),
); );
} }

Loading…
Cancel
Save