Merge branch 'master' into Main_App_Replacement

# Conflicts:
#	ios/Runner.xcodeproj/project.pbxproj
#	pubspec.yaml
Main_App_Replacement
Haroon Amjad 6 days ago
commit 53306fe306

@ -0,0 +1,12 @@
{
"key": "none",
"enabled": false,
"start_date": null,
"end_date": null,
"primary_color": null,
"secondary_color": null,
"background_opacity_image": null,
"background_image_opacity": 0.2,
"header_image": null
}

@ -20,7 +20,5 @@
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>13.0</string>
</dict>
</plist>

@ -5,6 +5,24 @@
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"

@ -138,8 +138,7 @@ class ApiClientImp implements ApiClient {
} else {}
if (body.containsKey('isDentalAllowedBackend')) {
body['isDentalAllowedBackend'] =
body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
body['isDentalAllowedBackend'] = body.containsKey('isDentalAllowedBackend') ? body['isDentalAllowedBackend'] ?? IS_DENTAL_ALLOWED_BACKEND : IS_DENTAL_ALLOWED_BACKEND;
}
if (!body.containsKey('IsPublicRequest')) {
@ -210,8 +209,8 @@ class ApiClientImp implements ApiClient {
body['TokenID'] = "@dm!n";
}
// body['TokenID'] = "@dm!n";
// body['PatientID'] = 5422188;
body['TokenID'] = "@dm!n";
// body['PatientID'] = 7000792;
// body['PatientID'] = 53320;
// body['PatientTypeID'] = 1;
// body['PatientOutSA'] = 0;
@ -271,11 +270,11 @@ class ApiClientImp implements ApiClient {
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'POST');
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_socket_exception');
return;
@ -283,19 +282,19 @@ class ApiClientImp implements ApiClient {
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
onFailure(message, -1, failureType: ConnectivityFailure(message));
_analytics.errorTracking.log(endPoint, error: "ClientException: $e");
// Enhanced Clarity logging
Clarity.setCustomTag('api_err_type', 'client_exception');
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'POST');
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_client_exception');
return;
} on TimeoutException catch (_) {
final message = LocaleKeys.networkTimeout.tr();
@ -307,36 +306,36 @@ class ApiClientImp implements ApiClient {
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'POST');
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_timeout');
return;
} catch (e) {
final message = LocaleKeys.networkUnknownError.tr();
onFailure(message, -1, failureType: ConnectivityFailure(message));
_analytics.errorTracking.log(endPoint, error: "UnknownException: $e");
// Enhanced Clarity logging for unknown errors
Clarity.setCustomTag('api_err_type', 'unknown_exception');
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'POST');
// Truncate exception message for Clarity
final exceptionMsg = e.toString();
final truncatedMsg = exceptionMsg.length > 252 ? '...${exceptionMsg.substring(exceptionMsg.length - 252)}' : exceptionMsg;
Clarity.setCustomTag('api_err_exception', truncatedMsg);
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_unknown_exception');
return;
}
@ -384,24 +383,19 @@ class ApiClientImp implements ApiClient {
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
statusCode,
failureType:
UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
failureType: UnAuthenticatedUserFailure(parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'] ?? "User is not Authenticated", url: url),
);
// logApiEndpointError(endPoint, "session logged out", statusCode);
}
if (isAllowAny) {
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['IsAuthenticated'] == null) {
if (parsed['isSMSSent'] == true) {
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['MessageStatus'] == 1) {
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else if (parsed['Result'] == 'OK') {
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
onFailure(
parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'],
@ -411,8 +405,7 @@ class ApiClientImp implements ApiClient {
logApiEndpointError(endPoint, parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage'], statusCode);
}
} else if (parsed['MessageStatus'] == 1 || parsed['SMSLoginRequired'] == true) {
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
}
// else if (parsed['IsAuthenticated'] == false) {
// onFailure(
@ -424,8 +417,7 @@ class ApiClientImp implements ApiClient {
// }
else if (parsed['MessageStatus'] == 2 && parsed['IsAuthenticated']) {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] == null && parsed['ErrorEndUserMessage'] == null) {
if (parsed['ErrorSearchMsg'] == null) {
@ -459,8 +451,7 @@ class ApiClientImp implements ApiClient {
}
} else {
if (parsed['SameClinicApptList'] != null) {
onSuccess(parsed, statusCode,
messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']);
} else {
if (parsed['message'] != null) {
onFailure(
@ -537,74 +528,74 @@ class ApiClientImp implements ApiClient {
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
onFailure(message, -1);
_analytics.errorTracking.log(endPoint, error: "SocketException: $e");
// Clarity logging for GET requests
Clarity.setCustomTag('api_err_type', 'socket_exception');
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'GET');
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_get_socket_exception');
return;
} on http.ClientException catch (e) {
final message = e.message.contains('Connection reset by peer') ? LocaleKeys.networkConnectionReset.tr() : LocaleKeys.networkErrorMessage.tr();
onFailure(message, -1);
_analytics.errorTracking.log(endPoint, error: "ClientException: $e");
Clarity.setCustomTag('api_err_type', 'client_exception');
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'GET');
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_get_client_exception');
return;
} on TimeoutException catch (_) {
final message = LocaleKeys.networkTimeout.tr();
onFailure(message, -1);
_analytics.errorTracking.log(endPoint, error: "TimeoutException");
Clarity.setCustomTag('api_err_type', 'timeout');
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'GET');
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_get_timeout');
return;
} catch (e) {
final message = LocaleKeys.networkUnknownError.tr();
onFailure(message, -1);
_analytics.errorTracking.log(endPoint, error: "UnknownException: $e");
Clarity.setCustomTag('api_err_type', 'unknown_exception');
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'GET');
final exceptionMsg = e.toString();
final truncatedMsg = exceptionMsg.length > 252 ? '...${exceptionMsg.substring(exceptionMsg.length - 252)}' : exceptionMsg;
Clarity.setCustomTag('api_err_exception', truncatedMsg);
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_get_unknown_exception');
return;
}
@ -614,18 +605,18 @@ class ApiClientImp implements ApiClient {
if (statusCode < 200 || statusCode >= 400) {
onFailure(LocaleKeys.networkErrorWhileFetching.tr(), statusCode);
logApiEndpointError(endPoint, LocaleKeys.networkErrorWhileFetching.tr(), statusCode);
// Clarity logging for GET status code errors
Clarity.setCustomTag('api_err_type', 'status_code_error');
Clarity.setCustomTag('api_err_url', _getEndofUrl(url.trim()));
Clarity.setCustomTag('api_err_endpoint', _getEndpointName(endPoint));
Clarity.setCustomTag('api_err_method', 'GET');
Clarity.setCustomTag('api_err_status', statusCode.toString());
if (_appState.isAuthenticated) {
Clarity.setCustomTag('api_err_user_id', _appState.getAuthenticatedUser()!.patientId.toString());
}
Clarity.sendCustomEvent('api_get_status_error');
} else {
var parsed = json.decode(utf8.decode(response.bodyBytes));
@ -813,7 +804,7 @@ class ApiClientImp implements ApiClient {
// Extract the last part of the endpoint path for cleaner tracking
const int clarityLimit = 100;
final cleanEndpoint = endpoint.split('/').last;
if (cleanEndpoint.length <= clarityLimit) return cleanEndpoint;
return '...${cleanEndpoint.substring(cleanEndpoint.length - (clarityLimit - 3))}';
}

@ -1,11 +1,12 @@
import 'package:amazon_payfort/amazon_payfort.dart';
import 'package:flutter_amazonpaymentservices/environment_type.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:myfatoorah_flutter/myfatoorah_flutter.dart';
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT
@ -44,12 +45,17 @@ class ApiConsts {
// var payFortEnvironment = FortEnvironment.test;
// var applePayMerchantId = "merchant.com.hmgwebservices.uat";
static var myFatoorahEnvironmentType = MFEnvironment.TEST;
static var myFatoorahCountry = MFCountry.SAUDIARABIA;
static setBackendURLs() {
switch (appEnvironmentType) {
case AppEnvironmentTypeEnum.prod:
baseUrl = "https://hmgwebservices.com/";
payFortEnvironment = FortEnvironment.production;
payFortEnvironmentType = EnvironmentType.production;
myFatoorahEnvironmentType = MFEnvironment.LIVE;
myFatoorahCountry = MFCountry.SAUDIARABIA;
applePayMerchantId = "merchant.com.hmgwebservices";
SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx";
TAMARA_URL = "https://mdlaboratories.com/tamaralive/Home/Checkout";
@ -66,6 +72,8 @@ class ApiConsts {
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
payFortEnvironmentType = EnvironmentType.sandbox;
myFatoorahEnvironmentType = MFEnvironment.TEST;
myFatoorahCountry = MFCountry.KUWAIT;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -77,12 +85,13 @@ class ApiConsts {
symptomsCheckerApi = symptomsCheckerApiUAT;
symptomsCheckerUsername = 'guest_user';
symptomsCheckerPassword = '123456';
break;
case AppEnvironmentTypeEnum.uat:
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
payFortEnvironmentType = EnvironmentType.sandbox;
myFatoorahEnvironmentType = MFEnvironment.TEST;
myFatoorahCountry = MFCountry.KUWAIT;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -100,6 +109,8 @@ class ApiConsts {
baseUrl = "https://webservices.hmg.com/";
payFortEnvironment = FortEnvironment.production;
payFortEnvironmentType = EnvironmentType.production;
myFatoorahEnvironmentType = MFEnvironment.LIVE;
myFatoorahCountry = MFCountry.SAUDIARABIA;
applePayMerchantId = "merchant.com.hmgwebservices";
SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx";
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -117,6 +128,8 @@ class ApiConsts {
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
payFortEnvironmentType = EnvironmentType.sandbox;
myFatoorahEnvironmentType = MFEnvironment.TEST;
myFatoorahCountry = MFCountry.KUWAIT;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -134,6 +147,8 @@ class ApiConsts {
baseUrl = "https://uat.hmgwebservices.com/";
payFortEnvironment = FortEnvironment.test;
payFortEnvironmentType = EnvironmentType.sandbox;
myFatoorahEnvironmentType = MFEnvironment.TEST;
myFatoorahCountry = MFCountry.KUWAIT;
applePayMerchantId = "merchant.com.hmgwebservices.uat";
SERVICE_URL = 'https://uat.hmgwebservices.com/HMGPayment/pages/SendPayFortRequest.aspx';
TAMARA_URL = "https://epharmacy.hmg.com/tamara/Home/Checkout";
@ -865,8 +880,7 @@ var GET_CUSTOMER_INFO = "VerifyCustomer";
//Pharmacy
var GET_PHARMACY_CATEGORISE =
'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0';
var GET_PHARMACY_CATEGORISE = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=0';
var GET_OFFERS_CATEGORISE = 'discountcategories';
var GET_OFFERS_PRODUCTS = 'offerproducts/';
var GET_CATEGORISE_PARENT = 'categories?fields=id,name,namen,description,image,localized_names,display_order,parent_category_id,is_leaf&parent_id=';

@ -48,12 +48,19 @@ class AppState {
bool isPaytabsEnabled = false;
bool isPayfortHostedPageEnabled = true;
bool isMyFatoorahEnabled = false;
// bool isMyFatoorahEnabled = true;
void setIsPaytabsEnabled(bool value) {
isPaytabsEnabled = value;
// isPaytabsEnabled = true;
}
void setIsMyFatoorahEnabled(bool value) {
// isMyFatoorahEnabled = value;
isMyFatoorahEnabled = true;
}
void setIsPayfortHostedPageEnabled(bool value) {
isPayfortHostedPageEnabled = value;
// isPayfortHostedPageEnabled = false;

@ -44,6 +44,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_rep
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_repo.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart';
import 'package:hmg_patient_app_new/features/myfatoorah/myfatoorah_view_model.dart';
import 'package:hmg_patient_app_new/features/notifications/notifications_repo.dart';
import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
import 'package:hmg_patient_app_new/features/offers_and_discounts/offers_and_discounts_repo.dart';
@ -84,7 +85,9 @@ import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/services/notification_service.dart';
import 'package:hmg_patient_app_new/services/permission_service.dart';
import 'package:hmg_patient_app_new/services/remote_config_service.dart';
import 'package:hmg_patient_app_new/services/security_service.dart';
import 'package:hmg_patient_app_new/features/occasion_theme/occasion_theme_view_model.dart';
import 'package:hmg_patient_app_new/core/services/turnstile_service.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_calendar_model.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
@ -380,8 +383,22 @@ class AppDependencies {
errorHandlerService: getIt(),
));
getIt.registerLazySingleton<MyFatoorahViewModel>(() => MyFatoorahViewModel(
appState: getIt(),
errorHandlerService: getIt(),
payfortRepo: getIt(),
));
getIt.registerLazySingleton<ClarityService>(
() => ClarityService(appState: getIt.get<AppState>()),
);
// Remote-config-driven "occasion" theming (National Day, Eid, etc.).
getIt.registerLazySingleton<RemoteConfigService>(() => RemoteConfigServiceImp(loggerService: getIt()));
getIt.registerLazySingleton<OccasionThemeViewModel>(() => OccasionThemeViewModel(
remoteConfigService: getIt(),
cacheService: getIt(),
loggerService: getIt(),
));
}
}

@ -0,0 +1,5 @@
class MyFatoorahConfig {
static const String configAPIKey = "SK_KWT_0auB76h3Frhj7aUBSZa6H5ei792eZoGAPh0bBxKsrgtyjqKgf38MPnZqzGqagErb";
}

@ -437,8 +437,8 @@ class Utils {
onCancelTap();
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
backgroundColor: AppColors.criticalActionColor,
borderColor: AppColors.criticalActionColor,
textColor: AppColors.whiteColor,
// icon: AppAssets.cancel,
// iconColor: AppColors.whiteColor,
@ -516,8 +516,8 @@ class Utils {
onCancelTap();
}
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
backgroundColor: AppColors.criticalActionColor,
borderColor: AppColors.criticalActionColor,
textColor: AppColors.whiteColor,
// icon: AppAssets.cancel,
// iconColor: AppColors.whiteColor,

@ -1248,6 +1248,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setUserBloodGroup = activation.patientBlodType ?? "N/A";
_appState.setIsPaytabsEnabled(Utils.havePrivilege(120));
_appState.setIsPayfortHostedPageEnabled(Utils.havePrivilege(121));
_appState.setIsMyFatoorahEnabled(Utils.havePrivilege(123));
// Refresh privileges after user switching to ensure correct privileges are loaded
if (isSwitchUser) {
@ -1797,6 +1798,7 @@ class AuthenticationViewModel extends ChangeNotifier {
_appState.setProjectsDetailList(projectDetailListModel);
_appState.setIsPaytabsEnabled(Utils.havePrivilege(120));
_appState.setIsPayfortHostedPageEnabled(Utils.havePrivilege(121));
_appState.setIsMyFatoorahEnabled(Utils.havePrivilege(123));
}
},
);

@ -209,12 +209,12 @@ class LabViewModel extends ChangeNotifier {
final result = await labRepo.getLabResultReportPDF(labOrder: labOrder);
result.fold(
(failure) async => await errorHandlerService.handleError(
failure: failure,
onOkPressed: () {
onError!(failure.message);
},
),
// (failure) async => await errorHandlerService.handleError(
(failure) async {
if (onError != null) {
onError(failure.message);
}
},
(apiResponse) {
if (apiResponse.messageStatus == 2) {
onError!(apiResponse.errorMessage!);

@ -772,9 +772,9 @@ class MyAppointmentsViewModel extends ChangeNotifier {
patientMyDoctorsList = apiResponse.data!;
isPatientMyDoctorsLoading = false;
if (!isTop8) {
isMyDoctorsDataToBeLoaded = false;
}
// if (!isTop8) {
isMyDoctorsDataToBeLoaded = false;
// }
notifyListeners();
if (onSuccess != null) {

@ -0,0 +1,197 @@
import 'dart:developer';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/myFatoorah_config.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_request_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/payfort_response_insert_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart';
import 'package:hmg_patient_app_new/generated/codegen_loader.g.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:myfatoorah_flutter/myfatoorah_flutter.dart';
class MyFatoorahViewModel extends ChangeNotifier {
final AppState appState;
final ErrorHandlerService errorHandlerService;
final PayfortRepo payfortRepo;
MyFatoorahViewModel({
required this.appState,
required this.errorHandlerService,
required this.payfortRepo,
});
// Payment Method ID
// 2: Credit/Debit Card
// 6: Mada
initiateCardPayment({
num amount = 10.0,
paymentDescription = "",
String displayCurrencyIso = MFCurrencyISO.KUWAIT_KWD,
required String paymentOption,
required String liveServiceID,
required String isScheduleLiveCare,
required String appointmentDate,
required String appointmentNo,
required String doctorID,
required String fileNumber,
required String serviceID,
required String projectID,
Function(MFGetPaymentStatusResponse)? onSuccess,
Function(String)? onError,
}) async {
LoaderBottomSheet.showLoader();
await MFSDK.init(MyFatoorahConfig.configAPIKey, ApiConsts.myFatoorahCountry, ApiConsts.myFatoorahEnvironmentType);
MFSDK.setUpActionBar(toolBarTitle: paymentDescription, toolBarTitleColor: "#ffffff", toolBarBackgroundColor: '#ED1C2B', isShowToolBar: true);
var request = MFExecutePaymentRequest(paymentMethodId: paymentOption.toLowerCase() == "mada" ? 6 : 2, invoiceValue: amount);
request.displayCurrencyIso = displayCurrencyIso;
request.customerName = ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? "Test test" : "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}";
request.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId}@HMG.com";
await MFSDK.executePayment(request, appState.isArabic() ? MFLanguage.ARABIC : MFLanguage.ENGLISH, (invoiceId) async {
PayfortRequestInsertModel payfortRequestInsertModel = PayfortRequestInsertModel();
payfortRequestInsertModel.commandType = "SALE";
payfortRequestInsertModel.amount = amount;
payfortRequestInsertModel.customerEmail = getIt.get<AppState>().getAuthenticatedUser()!.emailAddress;
payfortRequestInsertModel.orderDescription = paymentDescription;
payfortRequestInsertModel.serviceID = serviceID;
payfortRequestInsertModel.paymentOption = paymentOption;
payfortRequestInsertModel.clientRequestID = invoiceId;
payfortRequestInsertModel.projectID = projectID;
payfortRequestInsertModel.fileNumber = getIt.get<AppState>().getAuthenticatedUser()!.patientId.toString();
payfortRequestInsertModel.customerName = "${getIt.get<AppState>().getAuthenticatedUser()!.firstName} ${getIt.get<AppState>().getAuthenticatedUser()!.lastName}";
payfortRequestInsertModel.currency = getIt.get<AppState>().getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
payfortRequestInsertModel.liveServiceID = liveServiceID;
payfortRequestInsertModel.latitude = getIt.get<AppState>().userLat.toString();
payfortRequestInsertModel.longitude = getIt.get<AppState>().userLong.toString();
payfortRequestInsertModel.merchantReference = invoiceId;
payfortRequestInsertModel.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
payfortRequestInsertModel.language = getIt.get<AppState>().isArabic() ? "ar" : "en";
payfortRequestInsertModel.integrationId = "6"; // PayTabs integration ID
payfortRequestInsertModel.isSchedule = isScheduleLiveCare;
payfortRequestInsertModel.appointmentDate = appointmentDate;
payfortRequestInsertModel.appointmentNo = appointmentNo;
payfortRequestInsertModel.doctorID = doctorID;
// Call request insert API before payment
await paytabsRequestInsert(payfortRequestInsertModel: payfortRequestInsertModel);
LoaderBottomSheet.hideLoader();
}).then((value) async {
var valJson = value.toString();
print(valJson);
// // Create PayfortResponseInsertModel (same as Payfort implementation)
PayfortResponseInsertModel payfortResponseInsertModel = PayfortResponseInsertModel();
payfortResponseInsertModel.fortId = value.invoiceTransactions!.first.transactionId ?? "";
payfortResponseInsertModel.commandType = "SALE";
// payfortResponseInsertModel.amount = paytabsTransactionResponseModel.tranTotal ?? 0;
payfortResponseInsertModel.amount = amount;
payfortResponseInsertModel.sadadOLP = "";
payfortResponseInsertModel.paymentOption = value.invoiceTransactions!.first.paymentGateway ?? "";
payfortResponseInsertModel.customerIP = "";
payfortResponseInsertModel.eCI = "";
payfortResponseInsertModel.responseMessage = value.invoiceTransactions!.first.transactionStatus ?? "";
payfortResponseInsertModel.cardNumber = value.invoiceTransactions!.first.cardNumber ?? "";
payfortResponseInsertModel.cardBin = "";
payfortResponseInsertModel.installmentsNumber = 0;
payfortResponseInsertModel.status = (value.invoiceStatus ?? "").toLowerCase() == "paid" ? "14" : "00";
payfortResponseInsertModel.merchantRef = value.invoiceId.toString();
payfortResponseInsertModel.isRefund = false;
payfortResponseInsertModel.remmeberMe = true;
payfortResponseInsertModel.reconciliationReference = "";
// Call response insert API after payment
await paytabsResponseInsert(payfortResponseInsertModel: payfortResponseInsertModel);
if ((value.invoiceStatus ?? "").toLowerCase() == "paid") {
if (onSuccess != null) {
onSuccess(value);
}
} else {
if (onError != null) {
onError(LocaleKeys.paymentFailedPleaseTryAgain.tr());
}
}
}).catchError((error) {
if (onError != null) {
onError(error.message);
}
});
}
executeApplePayPayment() {
MFApplepay.executeApplePayPayment(onInvoiceCreated: (invoiceID) {
log(
invoiceID.toString(),
);
}).then((value) {
log(
value.toString(),
);
}).catchError((error) {
log(error.message);
});
}
initApplePayPayment(num invoiceValue) async {
await MFSDK.init(MyFatoorahConfig.configAPIKey, ApiConsts.myFatoorahCountry, ApiConsts.myFatoorahEnvironmentType);
await MFSDK.initSession(MFInitiateSessionRequest(), MFLanguage.ENGLISH).then((value) async {
var executePaymentRequest = MFExecutePaymentRequest(invoiceValue: invoiceValue, sessionId: value.sessionId, displayCurrencyIso: MFCurrencyISO.SAUDIARABIA_SAR);
MFApplepay.setupApplePay(value, executePaymentRequest, MFLanguage.ENGLISH, merchantName: "Dr Sulaiman Al Habib");
}).catchError((error) {
log(error.message);
});
}
Future<void> paytabsRequestInsert({required PayfortRequestInsertModel payfortRequestInsertModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.payfortRequestInsert(payfortRequestInsertModel: payfortRequestInsertModel);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
Future<void> paytabsResponseInsert({required PayfortResponseInsertModel payfortResponseInsertModel, Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await payfortRepo.payfortResponseInsert(payfortResponseInsertModel: payfortResponseInsertModel);
result.fold(
(failure) async => await errorHandlerService.handleError(failure: failure),
(apiResponse) {
if (apiResponse.messageStatus == 2) {
// dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {});
} else if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
}
},
);
}
}

@ -0,0 +1,123 @@
import 'package:flutter/material.dart';
/// Model representing a remotely-configurable "occasion" theme
/// (e.g. Saudi National Day, Founding Day, Eid, Ramadan, ...).
///
/// Everything here is fully data-driven — nothing about a specific
/// occasion (name, color, dates, banner asset) is hardcoded in the app.
/// All values are supplied via Firebase Remote Config (or the bundled
/// default JSON fallback used before the first successful fetch).
@immutable
class OccasionConfigModel {
/// Unique identifier of the occasion (e.g. "national_day"). Used only
/// for logging/analytics — no app logic branches on this value.
final String key;
/// Master kill-switch. When false, the app must look exactly like the
/// default/current theme, regardless of any other field below.
final bool enabled;
/// Optional activation window. When both are present, the theme is only
/// considered active if `DateTime.now()` falls within [startDate, endDate].
/// When null, the [enabled] flag alone controls activation.
final DateTime? startDate;
final DateTime? endDate;
/// Hex string (e.g. "#00894A") for the occasion's primary brand color.
/// When null, the app's default primary color is preserved.
final String? primaryColorHex;
/// Remote (or bundled asset path) image rendered as a decorative
/// background/watermark, drawn using [backgroundImageOpacity].
final String? backgroundOpacityImage;
/// Opacity (0.0 - 1.0) applied to [backgroundOpacityImage].
final double backgroundImageOpacity;
/// Remote (or bundled asset path) image rendered as the occasion's main
/// header/banner (e.g. the National Day greeting banner), drawn at full
/// opacity on top of [backgroundOpacityImage].
final String? headerImage;
const OccasionConfigModel({
required this.key,
required this.enabled,
this.startDate,
this.endDate,
this.primaryColorHex,
this.backgroundOpacityImage,
this.backgroundImageOpacity = 1.0,
this.headerImage,
});
/// Fully inert config — identical to the app's current/default look.
factory OccasionConfigModel.disabled() => const OccasionConfigModel(
key: 'none',
enabled: false,
startDate: null,
endDate: null,
primaryColorHex: null,
backgroundOpacityImage: null,
backgroundImageOpacity: 1.0,
headerImage: null,
);
factory OccasionConfigModel.fromJson(Map<String, dynamic> json) {
return OccasionConfigModel(
key: (json['key'] as String?) ?? 'none',
enabled: (json['enabled'] as bool?) ?? false,
startDate: _parseDate(json['start_date']),
endDate: _parseDate(json['end_date']),
primaryColorHex: json['primary_color'] as String?,
backgroundOpacityImage: json['background_opacity_image'] as String?,
backgroundImageOpacity: (json['background_image_opacity'] as num?)?.toDouble() ?? 1.0,
headerImage: json['header_image'] as String?,
);
}
static DateTime? _parseDate(dynamic value) {
if (value == null) return null;
if (value is String && value.isNotEmpty) {
return DateTime.tryParse(value);
}
return null;
}
/// Whether "now" falls inside the configured activation window.
/// If no window is configured, the [enabled] flag alone decides.
bool isWithinActiveWindow([DateTime? now]) {
final current = now ?? DateTime.now();
if (startDate != null && current.isBefore(startDate!)) return false;
if (endDate != null && current.isAfter(endDate!)) return false;
return true;
}
/// Parsed [Color] from [primaryColorHex], or null if not configured.
Color? get primaryColor => _hexToColor(primaryColorHex);
/// Lighter/translucent variant of [primaryColor] — 11% opacity of the same
/// hue (e.g. for chip/badge backgrounds). Derived automatically from
/// [primaryColor]; not an independent remote-config field. Null unless
/// [primaryColor] is configured.
Color? get secondaryColor => primaryColor?.withValues(alpha: 0.11);
static Color? _hexToColor(String? hex) {
if (hex == null || hex.isEmpty) return null;
var value = hex.trim().replaceFirst('#', '');
if (value.length == 6) value = 'FF$value';
final parsed = int.tryParse(value, radix: 16);
return parsed != null ? Color(parsed) : null;
}
Map<String, dynamic> toJson() => {
'key': key,
'enabled': enabled,
'start_date': startDate?.toIso8601String(),
'end_date': endDate?.toIso8601String(),
'primary_color': primaryColorHex,
'background_opacity_image': backgroundOpacityImage,
'background_image_opacity': backgroundImageOpacity,
'header_image': headerImage,
};
}

@ -0,0 +1,109 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart' show Color;
import 'package:hmg_patient_app_new/features/occasion_theme/occasion_config_model.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/remote_config_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
/// Drives the fully remote-config-based "occasion" theming (National Day,
/// Founding Day, Eid, Ramadan, ...). Nothing about a specific occasion is
/// hardcoded here — every color/asset/date comes from [OccasionConfigModel],
/// which is parsed from Firebase Remote Config.
///
/// Mirrors the existing dark-mode pattern in ProfileSettingsViewModel:
/// a static flag on [AppColors] is kept in sync so every widget using
/// AppColors.primaryRedColor (even ones with no BuildContext/Provider
/// access) reflects the active occasion instantly.
class OccasionThemeViewModel extends ChangeNotifier {
static const String _cachedConfigKey = 'cached_occasion_config_json';
final RemoteConfigService _remoteConfigService;
final CacheService _cacheService;
final LoggerService _loggerService;
OccasionThemeViewModel({
required RemoteConfigService remoteConfigService,
required CacheService cacheService,
required LoggerService loggerService,
}) : _remoteConfigService = remoteConfigService,
_cacheService = cacheService,
_loggerService = loggerService;
OccasionConfigModel _config = OccasionConfigModel.disabled();
OccasionConfigModel get config => _config;
/// True only when enabled AND (if a window is configured) within it.
bool get isOccasionActive => _config.enabled && _config.isWithinActiveWindow();
String? get backgroundOpacityImage => isOccasionActive ? _config.backgroundOpacityImage : null;
double get backgroundImageOpacity => _config.backgroundImageOpacity;
/// The occasion's main header/banner image (e.g. National Day greeting
/// banner), rendered at full opacity. Null unless an occasion is active.
String? get headerImage => isOccasionActive ? _config.headerImage : null;
/// Lighter/translucent variant of the active occasion's primary color
/// (11% opacity) — e.g. for chip/badge backgrounds. Null unless an
/// occasion is active.
Color? get secondaryColor => isOccasionActive ? _config.secondaryColor : null;
/// Call once at app startup (before the first frame) to restore the last
/// known-good config instantly (offline-safe), then kick off a remote
/// fetch in the background.
Future<void> initialize() async {
_restoreCachedConfig();
_applyToAppColors();
try {
await _remoteConfigService.initialize();
await refresh();
} catch (ex) {
_loggerService.logError('OccasionThemeViewModel.initialize error: $ex');
}
}
/// Fetches the latest occasion config from Firebase Remote Config and
/// applies it. Safe to call again later (e.g. on app resume) to pick up
/// changes without requiring an app update.
Future<void> refresh() async {
try {
await _remoteConfigService.fetchAndActivate();
final rawJson = _remoteConfigService.occasionConfigRawJson;
_config = OccasionConfigModel.fromJson(json.decode(rawJson) as Map<String, dynamic>);
await _cacheService.saveString(key: _cachedConfigKey, value: rawJson);
_applyToAppColors();
notifyListeners();
} catch (ex) {
_loggerService.logError('OccasionThemeViewModel.refresh error: $ex');
// Keep whatever config was already applied (cached/disabled) — never
// let a remote-config failure change the app's appearance unexpectedly.
}
}
void _restoreCachedConfig() {
try {
final cachedJson = _cacheService.getString(key: _cachedConfigKey);
if (cachedJson == null || cachedJson.isEmpty) {
_config = OccasionConfigModel.disabled();
return;
}
_config = OccasionConfigModel.fromJson(json.decode(cachedJson) as Map<String, dynamic>);
} catch (ex) {
_loggerService.logError('OccasionThemeViewModel._restoreCachedConfig error: $ex');
_config = OccasionConfigModel.disabled();
}
}
/// Keeps AppColors in sync so non-Provider call sites (AppColors.primaryRedColor)
/// pick up the occasion color exactly like the existing dark-mode flag does.
void _applyToAppColors() {
AppColors.isOccasionThemeActive = isOccasionActive;
AppColors.occasionPrimaryColor = _config.primaryColor;
AppColors.occasionSecondaryColor = _config.secondaryColor;
}
}

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
@ -35,7 +36,9 @@ import 'package:hmg_patient_app_new/features/my_appointments/appointment_rating_
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_invoices/my_invoices_view_model.dart';
import 'package:hmg_patient_app_new/features/myfatoorah/myfatoorah_view_model.dart';
import 'package:hmg_patient_app_new/features/notifications/notifications_view_model.dart';
import 'package:hmg_patient_app_new/features/occasion_theme/occasion_theme_view_model.dart';
import 'package:hmg_patient_app_new/features/offers_and_discounts/offers_and_discounts_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart';
import 'package:hmg_patient_app_new/features/paytabs/paytabs_view_model.dart';
@ -167,6 +170,14 @@ Future<void> callInitializations() async {
// Restore persisted accessibility settings before the first frame.
getIt.get<ProfileSettingsViewModel>().loadAccessibilitySettings();
// Restore last-known occasion theme instantly (offline-safe, synchronous)
// so the very first frame is already correct, then let the Remote Config
// fetch happen in the background without blocking app startup. Fully
// disabled by default — the app looks exactly as it does today until
// Remote Config enables an occasion (e.g. National Day) on the server side.
final occasionThemeViewModel = getIt.get<OccasionThemeViewModel>();
unawaited(occasionThemeViewModel.initialize());
}
void main() async {
@ -304,9 +315,15 @@ void main() async {
ChangeNotifierProvider<PayTabsViewModel>(
create: (_) => getIt.get<PayTabsViewModel>(),
),
ChangeNotifierProvider<MyFatoorahViewModel>(
create: (_) => getIt.get<MyFatoorahViewModel>(),
),
ChangeNotifierProvider<RefundRequestViewModel>(
create: (_) => getIt.get<RefundRequestViewModel>(),
)
),
ChangeNotifierProvider<OccasionThemeViewModel>(
create: (_) => getIt.get<OccasionThemeViewModel>(),
),
], child: MyApp()),
), // Wrap your app
);
@ -374,11 +391,11 @@ class MyApp extends StatelessWidget {
return Sizer(
builder: (context, orientation, deviceType) {
// SizeConfig().init(constraints, orientation);
return Consumer<ProfileSettingsViewModel>(
builder: (context, profileVm, _) {
return Consumer2<ProfileSettingsViewModel, OccasionThemeViewModel>(
builder: (context, profileVm, occasionThemeVm, _) {
final isArabic = EasyLocalization.of(context)?.locale.languageCode == "ar";
return MaterialApp(
key: ValueKey('app_theme_${profileVm.isDarkMode}'),
key: ValueKey('app_theme_${profileVm.isDarkMode}_${occasionThemeVm.isOccasionActive}_${occasionThemeVm.config.key}'),
// title: 'Dr. AlHabib',
title: 'Dr. AlHabib Beta',
builder: (context, mchild) {

@ -989,7 +989,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
openDoctorScheduleCalendar();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
borderColor: Colors.transparent,
textColor: Colors.white,
fontSize: 16.f,
fontWeight: FontWeight.w600,
@ -1097,7 +1097,7 @@ class _AppointmentDetailsPageState extends State<AppointmentDetailsPage> {
openDoctorScheduleCalendar();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
borderColor: Colors.transparent,
textColor: Colors.white,
fontSize: 16.f,
fontWeight: FontWeight.w600,

@ -67,8 +67,8 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
SizedBox(height: 16.h),
CustomTabBar(
initialIndex: myAppointmentsVM.selectedTabIndex,
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.secondaryLightRedColor,
tabs: [
// CustomTabBarModel(null, LocaleKeys.allAppt.tr(context: context)),
CustomTabBarModel(null, LocaleKeys.upcoming.tr(context: context)),
@ -269,9 +269,9 @@ class _MyAppointmentsPageState extends State<MyAppointmentsPage> {
),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
isBold: true,
borderRadius: 12.r,

@ -84,8 +84,7 @@ class AppointmentDoctorCard extends StatelessWidget {
children: [
Utils.buildSvgWithAssets(icon: AppAssets.rating_icon, width: 15.h, height: 15.h, iconColor: AppColors.ratingColorYellow),
SizedBox(height: 2.h),
"${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? 0.0}"
.toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true),
"${patientAppointmentHistoryResponseModel.decimalDoctorRate ?? 0.0}".toText11(isBold: true, color: AppColors.textColor, isEnglishOnly: true),
],
),
).circle(100),
@ -102,11 +101,7 @@ class AppointmentDoctorCard extends StatelessWidget {
children: [
Expanded(
child: (patientAppointmentHistoryResponseModel.doctorNameObj ?? "").toText16(
isBold: true,
isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? ""),
textOverflow: TextOverflow.ellipsis,
maxlines: 2
),
isBold: true, isEnglishOnly: !Utils.isArabicText(patientAppointmentHistoryResponseModel.doctorNameObj ?? ""), textOverflow: TextOverflow.ellipsis, maxlines: 2),
),
SizedBox(width: 12.w),
// (patientAppointmentHistoryResponseModel.doctorNationalityFlagURL != null &&
@ -142,18 +137,17 @@ class AppointmentDoctorCard extends StatelessWidget {
child: AppCustomChipWidget(
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
icon: AppAssets.doctor_calendar_icon,
richText: "${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${(patientAppointmentHistoryResponseModel.startTime != null && patientAppointmentHistoryResponseModel.startTime!.isNotEmpty) ? patientAppointmentHistoryResponseModel.startTime!.substring(0, 5) : DateUtil.formatDateToTimeLang(DateUtil.convertStringToDateSaudiTimezone(patientAppointmentHistoryResponseModel.appointmentDate!, patientAppointmentHistoryResponseModel.projectID), false)}"
.toText10(isBold: true),
richText:
"${DateUtil.formatDateToDate(DateUtil.convertStringToDate(patientAppointmentHistoryResponseModel.appointmentDate), false)} ${(patientAppointmentHistoryResponseModel.startTime != null && patientAppointmentHistoryResponseModel.startTime!.isNotEmpty) ? patientAppointmentHistoryResponseModel.startTime!.substring(0, 5) : DateUtil.formatDateToTimeLang(DateUtil.convertStringToDateSaudiTimezone(patientAppointmentHistoryResponseModel.appointmentDate!, patientAppointmentHistoryResponseModel.projectID), false)}"
.toText10(isBold: true),
),
),
),
AppCustomChipWidget(
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
icon: !(patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ? AppAssets.walkin_appointment_icon
: AppAssets.small_livecare_icon,
icon: !(patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ? AppAssets.walkin_appointment_icon : AppAssets.small_livecare_icon,
iconColor: !(patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ? AppColors.textColor : Colors.white,
labelText: (patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ? LocaleKeys.livecare.tr(context: context)
: LocaleKeys.walkin.tr(context: context),
labelText: (patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ? LocaleKeys.livecare.tr(context: context) : LocaleKeys.walkin.tr(context: context),
backgroundColor: !(patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ? AppColors.greyColor : AppColors.successColor,
textColor: !(patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false) ? AppColors.textColor : Colors.white,
),
@ -163,13 +157,12 @@ class AppointmentDoctorCard extends StatelessWidget {
),
),
(patientAppointmentHistoryResponseModel.isLiveCareAppointment! ||
patientAppointmentHistoryResponseModel.isExecludeDoctor! ||
patientAppointmentHistoryResponseModel.isExecludeDoctor! ||
patientAppointmentHistoryResponseModel.isClinicReBookingAllowed! == false ||
patientAppointmentHistoryResponseModel.isActiveDoctor! == false ||
!Utils.isClinicAllowedForRebook(patientAppointmentHistoryResponseModel.clinicID))
!Utils.isClinicAllowedForRebook(patientAppointmentHistoryResponseModel.clinicID))
? SizedBox.shrink()
: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown)
.onPress(() async {
: Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_icon, width: 20.h, height: 20.h, fit: BoxFit.scaleDown).onPress(() async {
// DoctorsListResponseModel selectedDoctor = DoctorsListResponseModel();
// selectedDoctor.doctorID = patientAppointmentHistoryResponseModel.doctorID;
// selectedDoctor.doctorImageURL = patientAppointmentHistoryResponseModel.doctorImageURL;
@ -270,7 +263,7 @@ class AppointmentDoctorCard extends StatelessWidget {
onAskDoctorTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
isBold: true,
@ -310,7 +303,7 @@ class AppointmentDoctorCard extends StatelessWidget {
onCancelTap();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
borderColor: Colors.transparent,
textColor: AppColors.whiteColor,
fontSize: 14.f,
isBold: true,
@ -329,7 +322,7 @@ class AppointmentDoctorCard extends StatelessWidget {
onRescheduleTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
isBold: true,

@ -142,9 +142,9 @@ class _AppointmentRatingWidgetState extends State<AppointmentRatingWidget> {
Expanded(
child: CustomButton(
text: LocaleKeys.later.tr(context: context),
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
onPressed: () {
Navigator.pop(context);
},

@ -94,17 +94,18 @@ class QuickLoginState extends State<QuickLogin> {
mainAxisAlignment: MainAxisAlignment.end,
children: [
Expanded(
child: CustomButton(
text: LocaleKeys.enableQuickLoginBtn.tr(context: context),
onPressed: () {
widget.onPressed();
},
backgroundColor: Color(0xffED1C2B),
borderColor: Color(0xffED1C2B),
textColor: Colors.white,
icon: AppAssets.apple_finder,
height: 56.h,
)),
child: CustomButton(
text: LocaleKeys.enableQuickLoginBtn.tr(context: context),
onPressed: () {
widget.onPressed();
},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: Colors.white,
icon: AppAssets.apple_finder,
height: 56.h,
),
),
],
),
SizedBox(
@ -120,9 +121,9 @@ class QuickLoginState extends State<QuickLogin> {
cacheService.saveBool(key: CacheConst.quickLoginEnabled, value: false);
Navigator.pop(context, "true");
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Colors.red,
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
height: 56.h,
// icon: "assets/images/svg/apple-finder.svg",
)),

@ -327,9 +327,9 @@ class _SavedLogin extends State<SavedLogin> with RouteAware {
// MaterialPageRoute(builder: (BuildContext context) => LandingNavigation())
// );
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,
@ -352,9 +352,9 @@ class _SavedLogin extends State<SavedLogin> with RouteAware {
MaterialPageRoute(builder: (BuildContext context) => LoginScreen()),
);
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 15.f,
fontWeight: FontWeight.w600,
borderRadius: 12.r,

@ -232,7 +232,7 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
Container(
padding: EdgeInsets.all(16.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.bgRedLightColor.withValues(alpha: 0.1),
color: AppColors.secondaryLightRedColorBase.withValues(alpha: 0.1),
borderRadius: 12.r,
hasShadow: false,
),

@ -106,8 +106,8 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
children: [
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.secondaryLightRedColor,
initialIndex: bookAppointmentsVM.selectedTabIndex,
tabs: [
CustomTabBarModel(null, LocaleKeys.general.tr(context: context)),
@ -326,9 +326,9 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
onPressed: () {
Navigator.of(context).push(CustomPageRoute(page: SearchDoctorByName()));
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
isBold: true,
padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),

@ -60,9 +60,7 @@ class DoctorProfilePage extends StatelessWidget {
doctorID: viewModel.doctorsProfileResponseModel.doctorID ?? 0,
isActive: viewModel.isFavouriteDoctor,
onSuccess: (response) {
Utils.showToast(viewModel.isFavouriteDoctor
? LocaleKeys.doctorAddedToFavourite.tr(context: context)
: LocaleKeys.doctorRemovedFromFavourite.tr(context: context));
Utils.showToast(viewModel.isFavouriteDoctor ? LocaleKeys.doctorAddedToFavourite.tr(context: context) : LocaleKeys.doctorRemovedFromFavourite.tr(context: context));
// Successfully added/removed favorite - refresh the favorites list
getIt.get<MyAppointmentsViewModel>().refreshFavouriteDoctors();
},
@ -101,14 +99,11 @@ class DoctorProfilePage extends StatelessWidget {
children: [
SizedBox(
width: isFoldable ? 250.w : 220.w,
child:
("${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorTitleForProfile} ${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName}")
.toString()
.toText24(isBold: true),
child: ("${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorTitleForProfile} ${bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName}")
.toString()
.toText24(isBold: true),
),
(bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.isNotEmpty
? bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.first
: "")
(bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.isNotEmpty ? bookAppointmentsViewModel.doctorsProfileResponseModel.specialty!.first : "")
.toString()
.toText18(isBold: true, color: AppColors.primaryRedColor),
],
@ -148,16 +143,12 @@ class DoctorProfilePage extends StatelessWidget {
children: [
Column(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.doctor_profile_rating_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false),
Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_rating_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false),
SizedBox(height: 16.h),
LocaleKeys.ratings.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor),
bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate.toString().toText16(
isBold: true,
color: AppColors.textColor,
isUnderLine: true,
decorationColor: AppColors.textColor,
fontFamily: "Poppins"),
bookAppointmentsViewModel.doctorsProfileResponseModel.decimalDoctorRate
.toString()
.toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor, fontFamily: "Poppins"),
],
).onPress(() {
bookAppointmentsViewModel.getDoctorRatingDetails();
@ -173,18 +164,12 @@ class DoctorProfilePage extends StatelessWidget {
SizedBox(width: 36.w),
Column(
children: [
Utils.buildSvgWithAssets(
icon: AppAssets.doctor_profile_reviews_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false),
Utils.buildSvgWithAssets(icon: AppAssets.doctor_profile_reviews_icon, width: 48.w, height: 48.h, fit: BoxFit.contain, applyThemeColor: false),
SizedBox(height: 16.h),
LocaleKeys.reviews.tr(context: context).toText12(isBold: true, color: AppColors.greyTextColor),
NumberFormat.decimalPattern("en_US")
.format(bookAppointmentsViewModel.doctorsProfileResponseModel.noOfPatientsRate ?? 0)
.toText16(
isBold: true,
color: AppColors.textColor,
isUnderLine: true,
decorationColor: AppColors.textColor,
fontFamily: "Poppins"),
.toText16(isBold: true, color: AppColors.textColor, isUnderLine: true, decorationColor: AppColors.textColor, fontFamily: "Poppins"),
],
).onPress(() {
bookAppointmentsViewModel.getDoctorRatingDetails();
@ -215,9 +200,7 @@ class DoctorProfilePage extends StatelessWidget {
SizedBox(height: 16.h),
LocaleKeys.information.tr(context: context).toText14(isBold: true, color: AppColors.textColor),
SizedBox(height: 6.h),
(bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo ?? "")
.trim()
.toText12(isBold: true, color: AppColors.greyTextColor),
(bookAppointmentsViewModel.doctorsProfileResponseModel.doctorProfileInfo ?? "").trim().toText12(isBold: true, color: AppColors.greyTextColor),
SizedBox(height: 24.h),
],
).paddingSymmetrical(24.h, 0.h),
@ -238,8 +221,7 @@ class DoctorProfilePage extends StatelessWidget {
bookAppointmentsViewModel.selectedDoctor.specialityN = bookAppointmentsViewModel.doctorsProfileResponseModel.specialty;
bookAppointmentsViewModel.selectedDoctor.name = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorName;
bookAppointmentsViewModel.selectedDoctor.doctorImageURL = bookAppointmentsViewModel.doctorsProfileResponseModel.doctorImageURL;
bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL =
bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL;
bookAppointmentsViewModel.selectedDoctor.nationalityFlagURL = bookAppointmentsViewModel.doctorsProfileResponseModel.nationalityFlagURL;
bookAppointmentsViewModel.selectedDoctor.clinicName = bookAppointmentsViewModel.doctorsProfileResponseModel.clinicDescription;
bookAppointmentsViewModel.selectedDoctor.projectName = bookAppointmentsViewModel.doctorsProfileResponseModel.projectName;

@ -80,8 +80,8 @@ class LaserAppointment extends StatelessWidget {
child: Column(
children: [
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.secondaryLightRedColor,
tabs: [
CustomTabBarModel(null, LocaleKeys.malE.tr()),
CustomTabBarModel(null, LocaleKeys.femaleGender.tr()),

@ -11,8 +11,10 @@ import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/cache_consts.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/myFatoorah_config.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/myfatoorah/myfatoorah_view_model.dart';
import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
@ -33,6 +35,8 @@ import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:myfatoorah_flutter/MFEnums.dart';
import 'package:myfatoorah_flutter/myfatoorah_flutter.dart';
import 'package:provider/provider.dart';
import 'package:smooth_corner/smooth_corner.dart';
@ -49,6 +53,9 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
late MyAppointmentsViewModel myAppointmentsViewModel;
late AppState appState;
late PayTabsViewModel paytabsViewModel;
late MyFatoorahViewModel myFatoorahViewModel;
late MFApplePayButton mfApplePayButton;
MyInAppBrowser? browser;
String selectedPaymentMethod = "";
@ -64,6 +71,11 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
Clarity.setCurrentScreenName('LiveCare Payment Page');
scheduleMicrotask(() {
payfortViewModel.initPayfortViewModel();
if (appState.isMyFatoorahEnabled) {
myFatoorahViewModel.initApplePayPayment(num.parse((immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total ?? "0.0")));
}
myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) {
if (myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel != null) {
if (num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total!) >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
@ -80,11 +92,15 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
@override
Widget build(BuildContext context) {
mfApplePayButton = MFApplePayButton(
applePayStyle: MFApplePayStyle(),
);
appState = getIt.get<AppState>();
myAppointmentsViewModel = Provider.of<MyAppointmentsViewModel>(context, listen: false);
immediateLiveCareViewModel = Provider.of<ImmediateLiveCareViewModel>(context, listen: false);
payfortViewModel = Provider.of<PayfortViewModel>(context, listen: false);
paytabsViewModel = Provider.of<PayTabsViewModel>(context, listen: false);
myFatoorahViewModel = Provider.of<MyFatoorahViewModel>(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Consumer<MyAppointmentsViewModel>(builder: (context, myAppointmentsVM, child) {
@ -131,7 +147,62 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "MADA";
if (appState.isPaytabsEnabled) {
if (appState.isMyFatoorahEnabled) {
myFatoorahViewModel.initiateCardPayment(
amount: num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total ?? "0.0"),
paymentDescription: "LiveCare Payment",
displayCurrencyIso: appState.getAuthenticatedUser()!.outSa == 0
? ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat
? MFCurrencyISO.KUWAIT_KWD
: MFCurrencyISO.SAUDIARABIA_SAR
: MFCurrencyISO.UAE_AED,
paymentOption: selectedPaymentMethod,
liveServiceID: immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID.toString(),
isScheduleLiveCare: "false",
appointmentDate: DateUtil.convertDateToString(DateTime.now()),
appointmentNo: "0",
doctorID: "0",
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
serviceID: ServiceTypeEnum.liveCareAppointment.getIdFromServiceEnum().toString(),
projectID: (ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12).toString(),
onSuccess: (response) async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr());
await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare(response.invoiceId.toString());
await immediateLiveCareViewModel.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
} else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Unknown error occurred..."),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
},
onError: (errMessage) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: errMessage.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
} else if (appState.isPaytabsEnabled) {
transID = Utils.getAppointmentTransID(
immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!,
ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12,
@ -298,7 +369,62 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
).paddingSymmetrical(16.h, 16.h),
).paddingSymmetrical(24.h, 0.h).onPress(() async {
selectedPaymentMethod = "VISA";
if (appState.isPaytabsEnabled) {
if (appState.isMyFatoorahEnabled) {
myFatoorahViewModel.initiateCardPayment(
amount: num.parse(immediateLiveCareViewModel.liveCareImmediateAppointmentFeesList.total ?? "0.0"),
paymentDescription: "LiveCare Payment",
displayCurrencyIso: appState.getAuthenticatedUser()!.outSa == 0
? ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat
? MFCurrencyISO.KUWAIT_KWD
: MFCurrencyISO.SAUDIARABIA_SAR
: MFCurrencyISO.UAE_AED,
paymentOption: selectedPaymentMethod,
liveServiceID: immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID.toString(),
isScheduleLiveCare: "false",
appointmentDate: DateUtil.convertDateToString(DateTime.now()),
appointmentNo: "0",
doctorID: "0",
fileNumber: appState.getAuthenticatedUser()!.patientId.toString(),
serviceID: ServiceTypeEnum.liveCareAppointment.getIdFromServiceEnum().toString(),
projectID: (ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12).toString(),
onSuccess: (response) async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr());
await immediateLiveCareViewModel.addNewCallRequestForImmediateLiveCare(response.invoiceId.toString());
await immediateLiveCareViewModel.getPatientLiveCareHistory();
LoaderBottomSheet.hideLoader();
if (immediateLiveCareViewModel.patientHasPendingLiveCareRequest) {
Navigator.pushAndRemoveUntil(
context,
CustomPageRoute(
page: LandingNavigation(),
),
(r) => false);
Navigator.of(context).push(
CustomPageRoute(
page: ImmediateLiveCarePendingRequestPage(),
),
);
} else {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: "Unknown error occurred..."),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
}
},
onError: (errMessage) {
showCommonBottomSheetWithoutHeight(
context,
child: Utils.getErrorWidget(loadingText: errMessage.toString()),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
);
});
} else if (appState.isPaytabsEnabled) {
transID = Utils.getAppointmentTransID(
immediateLiveCareViewModel.immediateLiveCareSelectedClinic.serviceID!,
ApiConsts.appEnvironmentType == AppEnvironmentTypeEnum.uat ? 15 : 12,
@ -610,16 +736,31 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
],
).paddingSymmetrical(24.h, 0.h),
Platform.isIOS
? Utils.buildSvgWithAssets(
icon: AppAssets.apple_pay_button,
width: 200.h,
height: 80.h,
fit: BoxFit.contain,
applyThemeColor: false
).paddingSymmetrical(24.h, 0.h).onPress(() {
?
// appState.isMyFatoorahEnabled
// ? SizedBox(
// height: 50.h,
// width: double.infinity,
// child: Stack(
// children: [
// Positioned.fill(child: mfApplePayButton),
// Positioned.fill(
// child: GestureDetector(
// behavior: HitTestBehavior.opaque,
// onTap: () {},
// ),
// ),
// ],
// ),
// ).paddingSymmetrical(24.h, 16.h)
// :
Utils.buildSvgWithAssets(icon: AppAssets.apple_pay_button, width: 200.h, height: 80.h, fit: BoxFit.contain, applyThemeColor: false)
.paddingSymmetrical(24.h, 0.h)
.onPress(() async {
// payfortVM.setIsApplePayConfigurationLoading(true);
if (Utils.havePrivilege(103)) {
startApplePay();
// startApplePay();
myFatoorahViewModel.executeApplePayPayment();
} else {
openPaymentURL("ApplePay");
}

@ -219,6 +219,9 @@ class DoctorCard extends StatelessWidget {
);
} else {
bookAppointmentsViewModel.setSelectedDoctor(doctorsListResponseModel);
GetClinicsListResponseModel selectedClinic = GetClinicsListResponseModel(
clinicID: doctorsListResponseModel.clinicID, clinicDescription: doctorsListResponseModel.clinicName, isLiveCareClinicAndOnline: false, liveCareServiceID: 0, liveCareClinicID: 0);
bookAppointmentsViewModel.setSelectedClinic(selectedClinic);
LoaderBottomSheet.showLoader();
bookAppointmentsViewModel.isLiveCareSchedule
? await bookAppointmentsViewModel.getLiveCareDoctorFreeSlots(
@ -308,9 +311,9 @@ class DoctorCard extends StatelessWidget {
});
}
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
isBold: true,
borderRadius: 10.r,

@ -1,5 +1,4 @@
import 'package:easy_localization/easy_localization.dart'
show tr, StringTranslateExtension;
import 'package:easy_localization/easy_localization.dart' show tr, StringTranslateExtension;
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/debouncer.dart';
@ -14,9 +13,9 @@ import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulan
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
class HospitalBottomSheetBody extends StatelessWidget {
final TextEditingController searchText;
final TextEditingController searchText ;
final Debouncer debouncer = Debouncer(milliseconds: 500);
final Debouncer debouncer = Debouncer(milliseconds: 500);
final int hmcCount;
final int hmgCount;
@ -26,11 +25,19 @@ class HospitalBottomSheetBody extends StatelessWidget {
final Function(HospitalsModel) onHospitalClicked;
final Function(String) onHospitalSearch;
HospitalBottomSheetBody({super.key, required this.hmcCount, required this.hmgCount, this.displayList, required this.selectedFacility, required this.onFacilityClicked, required this.onHospitalClicked, required this.onHospitalSearch, required this.searchText});
HospitalBottomSheetBody(
{super.key,
required this.hmcCount,
required this.hmgCount,
this.displayList,
required this.selectedFacility,
required this.onFacilityClicked,
required this.onHospitalClicked,
required this.onHospitalSearch,
required this.searchText});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -39,13 +46,12 @@ class HospitalBottomSheetBody extends StatelessWidget {
hintText: LocaleKeys.searchHospital.tr(),
controller: searchText,
onChange: (value) {
debouncer.run((){
onHospitalSearch(value??"");
debouncer.run(() {
onHospitalSearch(value ?? "");
});
},
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
@ -58,26 +64,26 @@ class HospitalBottomSheetBody extends StatelessWidget {
),
SizedBox(height: 24.h),
TypeSelectionWidget(
selectedFacility:selectedFacility ,
selectedFacility: selectedFacility,
hmcCount: hmcCount.toString(),
hmgCount: hmgCount.toString(),
onitemClicked: (selectedValue){
onitemClicked: (selectedValue) {
onFacilityClicked(selectedValue);
},
),
SizedBox(height: 21.h),
SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated(
itemBuilder: (_, index)
{
height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated(
itemBuilder: (_, index) {
var hospital = displayList?[index];
return HospitalListItem(
hospitalData: Utils.convertToPatientDoctorAppointmentList(hospital),
isLocationEnabled: true,
).onPress(() {
onHospitalClicked(hospital!);
});},
).onPress(() {
onHospitalClicked(hospital!);
});
},
separatorBuilder: (_, __) => SizedBox(
height: 16.h,
),

@ -9,192 +9,190 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
class LandingPageData {
static List<ServiceCardData> get getNotLoggedInServiceCardsList => [
ServiceCardData(
serviceName: "emergency",
icon: AppAssets.emergency_services_icon,
title: getIt.get<AppState>().isArabic()?LocaleKeys.services3 :LocaleKeys.emergency2,
subtitle: getIt.get<AppState>().isArabic()?LocaleKeys.emergency2:LocaleKeys.services3,
backgroundColor: AppColors.primaryRedColor,
iconColor: Colors.white,
textColor: Colors.white,
isBold: true,
),
ServiceCardData(
serviceName: "search_doctor",
icon: AppAssets.search_doctor_icon,
title: LocaleKeys.finda,
subtitle: LocaleKeys.doctor,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "health_calculators_and_converts",
icon: AppAssets.health_calculators_icon,
title: getIt.get<AppState>().isArabic()? LocaleKeys.calculators:LocaleKeys.health ,
subtitle: getIt.get<AppState>().isArabic()? LocaleKeys.health:LocaleKeys.calculators,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "health_converters",
icon: AppAssets.health_converters_icon,
title: getIt.get<AppState>().isArabic()? LocaleKeys.converters:LocaleKeys.health,
subtitle: getIt.get<AppState>().isArabic()? LocaleKeys.health:LocaleKeys.converters,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
// ServiceCardData(
// serviceName: "parking_guide",
// icon: AppAssets.car_parking_icon,
// title: getIt.get<AppState>().isArabic()? LocaleKeys.guide:LocaleKeys.parking2,
// subtitle: getIt.get<AppState>().isArabic()? LocaleKeys.parking2:LocaleKeys.guide,
// backgroundColor: AppColors.whiteColor,
// iconColor: AppColors.blackColor,
// textColor: AppColors.blackColor,
// isBold: false,
// ),
ServiceCardData(
serviceName: "indoor_navigation",
icon: AppAssets.indoor_nav_icon,
title: LocaleKeys.indoor,
subtitle: LocaleKeys.thehospital,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
];
ServiceCardData(
serviceName: "emergency",
icon: AppAssets.emergency_services_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.services3 : LocaleKeys.emergency2,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.emergency2 : LocaleKeys.services3,
backgroundColor: AppColors.primaryRedColorBase,
iconColor: Colors.white,
textColor: Colors.white,
isBold: true,
),
ServiceCardData(
serviceName: "search_doctor",
icon: AppAssets.search_doctor_icon,
title: LocaleKeys.finda,
subtitle: LocaleKeys.doctor,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "health_calculators_and_converts",
icon: AppAssets.health_calculators_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.calculators : LocaleKeys.health,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.health : LocaleKeys.calculators,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "health_converters",
icon: AppAssets.health_converters_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.converters : LocaleKeys.health,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.health : LocaleKeys.converters,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
// ServiceCardData(
// serviceName: "parking_guide",
// icon: AppAssets.car_parking_icon,
// title: getIt.get<AppState>().isArabic()? LocaleKeys.guide:LocaleKeys.parking2,
// subtitle: getIt.get<AppState>().isArabic()? LocaleKeys.parking2:LocaleKeys.guide,
// backgroundColor: AppColors.whiteColor,
// iconColor: AppColors.blackColor,
// textColor: AppColors.blackColor,
// isBold: false,
// ),
ServiceCardData(
serviceName: "indoor_navigation",
icon: AppAssets.indoor_nav_icon,
title: LocaleKeys.indoor,
subtitle: LocaleKeys.thehospital,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
];
List<ServiceCardData> get getLoggedInServiceCardsList => [
ServiceCardData(
serviceName: "emergency",
icon: AppAssets.emergency_services_icon,
title: getIt.get<AppState>().isArabic()?LocaleKeys.services3 :LocaleKeys.emergency2,
subtitle: getIt.get<AppState>().isArabic()?LocaleKeys.emergency2:LocaleKeys.services3,
backgroundColor: AppColors.primaryRedColor,
iconColor: Colors.white,
textColor: Colors.white,
isBold: true,
),
ServiceCardData(
serviceName: "lab_results",
icon: AppAssets.home_lab_result_icon,
title: getIt.get<AppState>().isArabic()?LocaleKeys.labSubtitle:LocaleKeys.lab,
subtitle: getIt.get<AppState>().isArabic()?LocaleKeys.lab:LocaleKeys.labSubtitle ,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "radiology_results",
icon: AppAssets.my_radiology_icon,
title: getIt.get<AppState>().isArabic()?LocaleKeys.labSubtitle:LocaleKeys.radiology,
subtitle: getIt.get<AppState>().isArabic()?LocaleKeys.radiology:LocaleKeys.labSubtitle ,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "prescriptions",
icon: AppAssets.my_prescription_icon,
title: getIt.get<AppState>().isArabic()?LocaleKeys.medicinesSubtitle:LocaleKeys.medicalKey,
subtitle: getIt.get<AppState>().isArabic()?LocaleKeys.medicalKey:LocaleKeys.medicinesSubtitle,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "insurance_update",
icon: AppAssets.insurance_update_icon,
title: getIt.get<AppState>().isArabic()?LocaleKeys.insurance :LocaleKeys.insurance,
subtitle: getIt.get<AppState>().isArabic()?LocaleKeys.medical: LocaleKeys.details2,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "my_doctors",
icon: AppAssets.my_doctors_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.myDoctorSubtitle2.tr() : "My",
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.myDoctor.tr() : "Doctors",
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "sick_leaves",
icon: AppAssets.my_sick_leave_icon,
title: getIt.get<AppState>().isArabic()?LocaleKeys.sickSubtitle:LocaleKeys.sick2,
subtitle: getIt.get<AppState>().isArabic()?LocaleKeys.sick2:LocaleKeys.sickSubtitle,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "my_invoice",
icon: AppAssets.invoiceListIcon,
title: getIt.get<AppState>().isArabic()?LocaleKeys.myDoctorSubtitle2:LocaleKeys.invoiceList2,
subtitle: getIt.get<AppState>().isArabic()?LocaleKeys.invoiceList2:LocaleKeys.myDoctorSubtitle2,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
];
ServiceCardData(
serviceName: "emergency",
icon: AppAssets.emergency_services_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.services3 : LocaleKeys.emergency2,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.emergency2 : LocaleKeys.services3,
backgroundColor: AppColors.primaryRedColorBase,
iconColor: Colors.white,
textColor: Colors.white,
isBold: true,
),
ServiceCardData(
serviceName: "lab_results",
icon: AppAssets.home_lab_result_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.labSubtitle : LocaleKeys.lab,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.lab : LocaleKeys.labSubtitle,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "radiology_results",
icon: AppAssets.my_radiology_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.labSubtitle : LocaleKeys.radiology,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.radiology : LocaleKeys.labSubtitle,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "prescriptions",
icon: AppAssets.my_prescription_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.medicinesSubtitle : LocaleKeys.medicalKey,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.medicalKey : LocaleKeys.medicinesSubtitle,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "insurance_update",
icon: AppAssets.insurance_update_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.insurance : LocaleKeys.insurance,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.medical : LocaleKeys.details2,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "my_doctors",
icon: AppAssets.my_doctors_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.myDoctorSubtitle2.tr() : "My",
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.myDoctor.tr() : "Doctors",
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "sick_leaves",
icon: AppAssets.my_sick_leave_icon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.sickSubtitle : LocaleKeys.sick2,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.sick2 : LocaleKeys.sickSubtitle,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
ServiceCardData(
serviceName: "my_invoice",
icon: AppAssets.invoiceListIcon,
title: getIt.get<AppState>().isArabic() ? LocaleKeys.myDoctorSubtitle2 : LocaleKeys.invoiceList2,
subtitle: getIt.get<AppState>().isArabic() ? LocaleKeys.invoiceList2 : LocaleKeys.myDoctorSubtitle2,
backgroundColor: AppColors.whiteColor,
iconColor: AppColors.blackColor,
textColor: AppColors.blackColor,
isBold: false,
),
];
static List<ServiceCardData> get getServiceCardsList => [
ServiceCardData(
serviceName: "livecare",
icon: AppAssets.small_livecare_icon,
title: LocaleKeys.liveCare,
subtitle: LocaleKeys.liveCareServiceDesc,
largeCardIcon: AppAssets.liveCareService,
backgroundColor: AppColors.successColor,
iconColor: AppColors.whiteColor,
isBold: false,
isPNG: false
),
// ServiceCardData(
// icon: AppAssets.homeBottom,
// title: "Dermatology",
// subtitle: "Explore our app, View our services and offers",
// largeCardIcon: AppAssets.homeBottom,
// backgroundColor: AppColors.primaryRedColor,
// isBold: false,
// ),
ServiceCardData(
serviceName: "home_health_care",
icon: AppAssets.homeBottom,
title: LocaleKeys.homeHealthCare,
subtitle: LocaleKeys.homeHealthCareText,
largeCardIcon: AppAssets.homeHealthCareService,
backgroundColor: AppColors.primaryRedColor,
iconColor: AppColors.whiteColor,
isBold: false,
isPNG: false
),
ServiceCardData(
serviceName: "pharmacy",
icon: AppAssets.defaultMEP, //359846
title: LocaleKeys.hmgPharmacy,
subtitle: LocaleKeys.hmgPharmacyText,
largeCardIcon: AppAssets.pharmacyService,
backgroundColor: AppColors.pharmacyBGColor,
iconColor: null,
isBold: true,
isPNG: true
),
];
ServiceCardData(
serviceName: "livecare",
icon: AppAssets.small_livecare_icon,
title: LocaleKeys.liveCare,
subtitle: LocaleKeys.liveCareServiceDesc,
largeCardIcon: AppAssets.liveCareService,
backgroundColor: AppColors.successColor,
iconColor: AppColors.whiteColor,
isBold: false,
isPNG: false),
// ServiceCardData(
// icon: AppAssets.homeBottom,
// title: "Dermatology",
// subtitle: "Explore our app, View our services and offers",
// largeCardIcon: AppAssets.homeBottom,
// backgroundColor: AppColors.primaryRedColor,
// isBold: false,
// ),
ServiceCardData(
serviceName: "home_health_care",
icon: AppAssets.homeBottom,
title: LocaleKeys.homeHealthCare,
subtitle: LocaleKeys.homeHealthCareText,
largeCardIcon: AppAssets.homeHealthCareService,
backgroundColor: AppColors.primaryRedColor,
iconColor: AppColors.whiteColor,
isBold: false,
isPNG: false),
ServiceCardData(
serviceName: "pharmacy",
icon: AppAssets.defaultMEP,
//359846
title: LocaleKeys.hmgPharmacy,
subtitle: LocaleKeys.hmgPharmacyText,
largeCardIcon: AppAssets.pharmacyService,
backgroundColor: AppColors.pharmacyBGColor,
iconColor: null,
isBold: true,
isPNG: true),
];
}

File diff suppressed because it is too large Load Diff

@ -92,7 +92,7 @@ class HabibWalletCard extends StatelessWidget {
),
SizedBox(width: 8.h),
(habibWalletVM.isWalletAmountHidden
? "••••"
? "****"
: NumberFormat.decimalPattern().format(habibWalletVM.habibWalletAmount).toString())
.toText32(isBold: true, isEnglishOnly: true)
.toShimmer2(isShow: habibWalletVM.isWalletAmountLoading, radius: 12.h, width: 80.h, height: 40.h),

@ -92,7 +92,7 @@ class LakumWalletCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
(habibWalletVM.isLakumPointsHidden
? "••••"
? "****"
: NumberFormat.decimalPattern()
.format(habibWalletVM.isLakumAccountInfoLoading
? 123

@ -0,0 +1,97 @@
import 'dart:developer';
import 'package:cached_network_image/cached_network_image.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/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/occasion_theme/occasion_theme_view_model.dart';
import 'package:provider/provider.dart';
/// Renders the occasion's main header/banner (`header_image`, e.g. the
/// National Day greeting banner) inline at the top of the landing page's
/// scrollable content, drawn at full opacity.
///
/// Fully data-driven: when no occasion is active (the default), this widget
/// renders nothing and takes zero space — the existing layout/UI is
/// completely unaffected.
///
/// See also [OccasionBackgroundWidget], which renders the decorative
/// backdrop image as a full-page background instead of inline content.
class OccasionBannerWidget extends StatelessWidget {
const OccasionBannerWidget({super.key});
@override
Widget build(BuildContext context) {
return Consumer<OccasionThemeViewModel>(
builder: (context, occasionThemeVm, _) {
final headerImageUrl = occasionThemeVm.headerImage;
final hasHeaderImage = occasionThemeVm.isOccasionActive && (headerImageUrl?.isNotEmpty ?? false);
if (!hasHeaderImage) {
return const SizedBox(height: 0);
}
return buildOccasionImage(headerImageUrl!).paddingOnly(bottom: 16.h);
},
);
}
}
/// Renders the occasion's decorative backdrop/watermark
/// (`background_opacity_image`) as a full-page background. Place this as
/// the first child of the landing page's outer [Stack] (behind the
/// scrollable content) so it fills the whole screen and sits behind
/// everything else, drawn at [OccasionThemeViewModel.backgroundImageOpacity].
///
/// Fully data-driven: when no occasion is active (the default), this widget
/// renders nothing — the existing layout/UI is completely unaffected.
class OccasionBackgroundWidget extends StatelessWidget {
const OccasionBackgroundWidget({super.key});
@override
Widget build(BuildContext context) {
return Consumer<OccasionThemeViewModel>(
builder: (context, occasionThemeVm, _) {
final backgroundImageUrl = occasionThemeVm.backgroundOpacityImage;
final hasBackgroundImage = occasionThemeVm.isOccasionActive && (backgroundImageUrl?.isNotEmpty ?? false);
if (!hasBackgroundImage) {
return const SizedBox.shrink();
}
return Positioned.fill(
child: Opacity(
opacity: occasionThemeVm.backgroundImageOpacity.clamp(0.0, 1.0),
child: buildOccasionImage(backgroundImageUrl!, isNeedPadding: true),
),
);
},
);
}
}
/// Remote-config image URLs may point to either an SVG (e.g. the National
/// Day banner) or a raster image (PNG/JPG). [CachedNetworkImage] cannot
/// decode SVG — it silently renders nothing — so SVG URLs are routed to
/// [SvgPicture.network] instead, based on the URL's file extension.
Widget buildOccasionImage(String url, {bool isNeedPadding = false}) {
final isSvg = url.split('?').first.toLowerCase().endsWith('.svg');
if (isSvg) {
return SvgPicture.network(
url,
width: double.infinity,
fit: BoxFit.contain,
placeholderBuilder: (_) => const SizedBox.shrink(),
);
}
return CachedNetworkImage(
imageUrl: url,
width: double.infinity,
fit: BoxFit.contain,
placeholder: (_, __) => const SizedBox.shrink(),
errorWidget: (_, __, error) {
return const SizedBox.shrink();
},
).paddingSymmetrical(isNeedPadding ? 24.w : 0, 0);
}

@ -38,7 +38,7 @@ class SmallServiceCard extends StatelessWidget {
final Color? backgroundColor;
final bool isBold;
SmallServiceCard({
const SmallServiceCard({
super.key,
this.icon = "",
this.serviceName = "",

@ -69,8 +69,8 @@ class InsuranceHistory extends StatelessWidget {
CustomButton(
text: insuranceVM.patientInsuranceCardHistoryList[index].statusDescription ?? "",
onPressed: () {},
backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
borderColor: AppColors.primaryRedColor.withOpacity(0.0),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 10,
isBold: true,

@ -117,7 +117,7 @@ class PatientInsuranceCard extends StatelessWidget {
}
},
backgroundColor: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!)
? AppColors.primaryRedColor.withOpacity(0.1)
? AppColors.secondaryLightRedColor
: insuranceViewModel.isInsuranceActive
? AppColors.successColor.withOpacity(0.1)
: AppColors.warningColorYellow.withOpacity(0.1),
@ -129,7 +129,7 @@ class PatientInsuranceCard extends StatelessWidget {
// iconColor: isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
// textColor: isInsuranceExpired ? AppColors.primaryRedColor : AppColors.successColor,
// iconSize: 12,
// backgroundColor: isInsuranceExpired ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
// backgroundColor: isInsuranceExpired ? AppColors.primaryRedColorBase.withOpacity(0.1) : AppColors.successColor.withOpacity(0.1),
// labelPadding: EdgeInsetsDirectional.only(start: -4.h, end: 8.h),
// ),
],

@ -14,8 +14,8 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import 'lab_order_by_test.dart';
class AlphabeticScroll extends StatefulWidget{
final List<String> alpahbetsAvailable;
class AlphabeticScroll extends StatefulWidget {
final List<String> alpahbetsAvailable;
final List<TestDetails> details;
final AppState appState;
final LabViewModel labViewModel;
@ -38,13 +38,13 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
Map<String, num> density = {};
Map<String, double> _offsetMap = {};
Map<String, GlobalKey> _offsetKeys = {};
Map<String, GlobalKey> _offsetKeys = {};
int _activeIndex = 0; // <-- Highlighted letter
@override
void initState() {
super.initState();
scheduleMicrotask((){
scheduleMicrotask(() {
_rebuildData();
});
itemPositionsListener.itemPositions.addListener(_onPositionChanged);
@ -61,9 +61,7 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
void _rebuildData() {
data.clear();
for (var char in widget.alpahbetsAvailable) {
data[char] = widget.details
.where((element) => element.description?.toLowerCase().startsWith(char.toLowerCase()) == true)
.toList();
data[char] = widget.details.where((element) => element.description?.toLowerCase().startsWith(char.toLowerCase()) == true).toList();
}
if (_activeIndex >= widget.alpahbetsAvailable.length) {
_activeIndex = 0;
@ -79,15 +77,12 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
// Get FIRST visible item (top-most)
final firstVisible = positions
.where((p) => p.itemTrailingEdge > 0) // visible
.reduce((min, p) =>
p.itemLeadingEdge < min.itemLeadingEdge ? p : min);
.reduce((min, p) => p.itemLeadingEdge < min.itemLeadingEdge ? p : min);
if(_activeIndex == firstVisible.index) return ;
if (_activeIndex == firstVisible.index) return;
setState(() {
_activeIndex = firstVisible.index;
});
print("Active index = $_activeIndex");
}
@override
@ -97,12 +92,8 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
}
void _scrollToLetter(String letter) async {
// itemScrollController.jumpTo(index:density[letter]?.toInt()??0, );
itemScrollController.scrollTo(
index: data.keys.toList().indexOf(letter).toInt()??0,
duration: Duration(seconds: 2),
curve: Curves.easeInOutCubic);
itemScrollController.scrollTo(index: data.keys.toList().indexOf(letter).toInt() ?? 0, duration: Duration(seconds: 2), curve: Curves.easeInOutCubic);
// final key = _offsetKeys[letter];
// if (key == null) return;
//
@ -134,58 +125,52 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
@override
Widget build(BuildContext context) {
return SizedBox(
width: MediaQuery.sizeOf(context).width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, // Add this
children: [
Expanded(
child: Column(
children: [
SizedBox(
height: (MediaQuery.sizeOf(context).height),
child: _buildList(widget.alpahbetsAvailable)
),
],
),
width: MediaQuery.sizeOf(context).width,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start, // Add this
children: [
Expanded(
child: Column(
children: [
SizedBox(height: (MediaQuery.sizeOf(context).height), child: _buildList(widget.alpahbetsAvailable)),
],
),
SizedBox(
width: 24.w,
height: MediaQuery.sizeOf(context).height-(70.h+ kToolbarHeight+100.h),
child: Column(
mainAxisSize: MainAxisSize.max, // Add this
mainAxisAlignment: MainAxisAlignment.center, // Changed from center to start
crossAxisAlignment: CrossAxisAlignment.center,
children:List.generate(widget.alpahbetsAvailable.length, (i) {
final isActive = (i == _activeIndex);
return GestureDetector(
onTap: () {
setState(() => _activeIndex = i);
_scrollToLetter(widget.alpahbetsAvailable[i]);
},
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 1.0, end: isActive ? 1.8 : 1.0),
duration: Duration(milliseconds: 120),
curve: Curves.easeOut,
builder: (_, scale, child) {
return Transform.scale(
scale: scale,
child: Opacity(
opacity: isActive ? 1.0 : 0.5,
child: widget.alpahbetsAvailable[i].toUpperCase().toText14(
color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor, isEnglishOnly: true
),
),
);
},
),
);
}
),
SizedBox(
width: 24.w,
height: MediaQuery.sizeOf(context).height - (70.h + kToolbarHeight + 100.h),
child: Column(
mainAxisSize: MainAxisSize.max, // Add this
mainAxisAlignment: MainAxisAlignment.center, // Changed from center to start
crossAxisAlignment: CrossAxisAlignment.center,
children: List.generate(widget.alpahbetsAvailable.length, (i) {
final isActive = (i == _activeIndex);
return GestureDetector(
onTap: () {
setState(() => _activeIndex = i);
_scrollToLetter(widget.alpahbetsAvailable[i]);
},
child: TweenAnimationBuilder<double>(
tween: Tween(begin: 1.0, end: isActive ? 1.8 : 1.0),
duration: Duration(milliseconds: 120),
curve: Curves.easeOut,
builder: (_, scale, child) {
return Transform.scale(
scale: scale,
child: Opacity(
opacity: isActive ? 1.0 : 0.5,
child: widget.alpahbetsAvailable[i].toUpperCase().toText14(color: !isActive ? AppColors.greyTextColor : AppColors.primaryRedColor, isEnglishOnly: true),
),
);
},
),
),
),
],
),
);
);
}),
),
),
],
),
);
// );
}
@ -205,35 +190,35 @@ class _AlphabetScrollPageState extends State<AlphabeticScroll> {
key: _offsetKeys[letter],
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children:
List.generate(items.length,(itemIndex)=>
AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabOrderByTest(
appState: getIt<AppState>(),
onTap: () {
if (items[itemIndex].model != null) {
widget.rangeViewModel.flush();
widget.labViewModel.getPatientLabResult(items[itemIndex].model!, items[itemIndex].description!,
(widget.appState.isArabic() ? items[itemIndex].testDescriptionAr! : items[itemIndex].testDescriptionEn!),"");
}
},
tests: items[itemIndex],
index: itemIndex,
isExpanded: true)),
),
))
children: List.generate(
items.length,
(itemIndex) => AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: LabOrderByTest(
appState: getIt<AppState>(),
onTap: () {
if (items[itemIndex].model != null) {
widget.rangeViewModel.flush();
widget.labViewModel.getPatientLabResult(items[itemIndex].model!, items[itemIndex].description!,
(widget.appState.isArabic() ? items[itemIndex].testDescriptionAr! : items[itemIndex].testDescriptionEn!), "");
}
},
tests: items[itemIndex],
index: itemIndex,
isExpanded: true)),
),
))
// ...items.indexed((item) =>
//
//
//
// )
),
),
);
},
);

@ -26,64 +26,65 @@ class LabOrderByTest extends StatelessWidget {
@override
build(BuildContext context) {
return AnimatedContainer(
key:key,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
'${tests!.description}'.toText16(isBold: true, isEnglishOnly: true),
SizedBox(height: 4.h),
(appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(isBold: true),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
richText: Row(
children: [
"${LocaleKeys.lastTested.tr(context: context)}: ".toText12(isBold: true),
Directionality(
textDirection: ui.TextDirection.ltr,child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false).toText12(isBold: true, isEnglishOnly: true))
],
),
backgroundColor: AppColors.greyLightColor,
textColor: AppColors.textColor,
key: key,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
margin: EdgeInsets.symmetric(vertical: 8.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: true),
child: Container(
key: ValueKey<int>(index),
padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 8.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
'${tests!.description}'.toText16(isBold: true, isEnglishOnly: true),
SizedBox(height: 4.h),
(appState.isArabic() ? tests!.testDescriptionAr : tests!.testDescriptionEn)!.toText12(isBold: true),
SizedBox(height: 8.h),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
AppCustomChipWidget(
richText: Row(
children: [
"${LocaleKeys.lastTested.tr(context: context)}: ".toText12(isBold: true),
Directionality(
textDirection: ui.TextDirection.ltr, child: DateUtil.formatDateToDate(DateUtil.convertStringToDate(tests!.createdOn), false).toText12(isBold: true, isEnglishOnly: true))
],
),
],
),
SizedBox(height: 16.h),
Row(
children: [
Expanded(child: Container()),
Expanded(
child: CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewResults.tr(context: context),
onPressed: () {
onTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 12,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
backgroundColor: AppColors.greyLightColor,
textColor: AppColors.textColor,
),
],
),
SizedBox(height: 16.h),
Row(
children: [
Expanded(child: Container()),
Expanded(
child: CustomButton(
icon: AppAssets.view_report_icon,
iconColor: AppColors.primaryRedColor,
iconSize: 16.h,
text: LocaleKeys.viewResults.tr(context: context),
onPressed: () {
onTap();
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
],
),
],
),
));
),
],
),
],
),
),
);
}
}

File diff suppressed because one or more lines are too long

@ -340,7 +340,7 @@ class _MedicalFilePageState extends State<MedicalFilePage> {
}
},
backgroundColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor.withOpacity(0.1)
? AppColors.secondaryLightRedColor
: insuranceVM.isInsuranceActive
? AppColors.successColor.withOpacity(0.1)
: AppColors.warningColorYellow.withOpacity(0.1),

@ -34,14 +34,13 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
super.initState();
medicalVM = context.read<MedicalFileViewModel>();
WidgetsBinding.instance.addPostFrameCallback((_) async {
if(medicalVM!.isFamilyFilesLoading) {
if (medicalVM!.isFamilyFilesLoading) {
LoaderBottomSheet.showLoader();
} else {
LoaderBottomSheet.hideLoader();
}
medicalVM?.onFamilyFileTabChange(0);
medicalVM?.getAllPendingRecordsByResponseId();
});
}
@ -71,6 +70,8 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
medicalVM!.addFamilyFile(otpTypeEnum: OTPTypeEnum.sms);
});
},
borderColor: AppColors.primaryRedColor,
backgroundColor: AppColors.primaryRedColor,
icon: AppAssets.add_icon,
fontSize: 16.f,
borderRadius: 12.r,
@ -104,9 +105,7 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
),
shouldRebuild: (previous, next) {
// Only rebuild if something actually changed
return previous.selectedIndex != next.selectedIndex ||
!identical(previous.patientFiles, next.patientFiles) ||
!identical(previous.pendingFiles, next.pendingFiles);
return previous.selectedIndex != next.selectedIndex || !identical(previous.patientFiles, next.patientFiles) || !identical(previous.pendingFiles, next.pendingFiles);
},
builder: (context, data, child) => getFamilyTabs(
index: data.selectedIndex,
@ -127,7 +126,6 @@ class _FamilyMedicalScreenState extends State<FamilyMedicalScreen> {
}) {
switch (index) {
case 0:
return FamilyCards(
profiles: patientFiles,
onSelect: (FamilyFileResponseModelLists profile) {

@ -165,67 +165,63 @@ class _FamilyCardsState extends State<FamilyCards> {
title: LocaleKeys.whoCanViewMyMedicalFile.tr(context: context).toText18(isBold: true),
expandedBackgroundColor: Colors.transparent,
initiallyExpanded: true,
children: [
SizedBox(height: 10.h),
manageFamily()
],
children: [SizedBox(height: 10.h), manageFamily()],
),
ExpandableListItem(title: LocaleKeys.notifications.tr(context: context).toText18(isBold: true), expandedBackgroundColor: Colors.transparent, initiallyExpanded: true, children: [
SizedBox(height: 10.h),
widget.profileViewList!.where((profile) => profile.isRequestFromMySide ?? false).isEmpty
? Utils.getNoDataWidget(context)
: ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: widget.profileViewList!.where((profile) => profile.isRequestFromMySide ?? false).length,
itemBuilder: (context, index) {
final mySideProfiles = widget.profileViewList!.where((profile) => profile.isRequestFromMySide ?? false).toList();
FamilyFileResponseModelLists profile = mySideProfiles[index];
return Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Opacity(
opacity: 1.0,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
AppCustomChipWidget(
labelText: profile.statusDescription ?? " N/A",
backgroundColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.active.toInt
? AppColors.lightGreenColor
: AppColors.lightGrayBGColor,
textColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor
: profile.status == FamilyFileEnum.active.toInt
? AppColors.textGreenColor
: AppColors.alertColor
),
SizedBox(height: 8.h),
Wrap(alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, runAlignment: WrapAlignment.start, spacing: 0.h, children: [
(profile.patientName ?? "").toText14(isBold: true, isCenter: false, maxlines: 1),
(getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false))
.toText12(isCenter: false, maxLine: 1, isBold: true, color: AppColors.greyTextColor),
]),
SizedBox(height: 8.h),
AppCustomChipWidget(
labelText: "Medical File: ${profile.responseId ?? "N/A"}",
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: widget.profileViewList!.where((profile) => profile.isRequestFromMySide ?? false).length,
itemBuilder: (context, index) {
final mySideProfiles = widget.profileViewList!.where((profile) => profile.isRequestFromMySide ?? false).toList();
FamilyFileResponseModelLists profile = mySideProfiles[index];
return Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.symmetric(vertical: 15.h, horizontal: 15.h),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24),
child: Opacity(
opacity: 1.0,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
AppCustomChipWidget(
labelText: profile.statusDescription ?? " N/A",
backgroundColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.active.toInt
? AppColors.lightGreenColor
: AppColors.lightGrayBGColor,
textColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor
: profile.status == FamilyFileEnum.active.toInt
? AppColors.textGreenColor
: AppColors.alertColor),
SizedBox(height: 8.h),
Wrap(alignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, runAlignment: WrapAlignment.start, spacing: 0.h, children: [
(profile.patientName ?? "").toText14(isBold: true, isCenter: false, maxlines: 1),
(getStatusTextByRequest(FamilyFileEnum.values.firstWhere((e) => e.toInt == profile.status), profile.isRequestFromMySide ?? false))
.toText12(isCenter: false, maxLine: 1, isBold: true, color: AppColors.greyTextColor),
]),
SizedBox(height: 8.h),
AppCustomChipWidget(
labelText: "Medical File: ${profile.responseId ?? "N/A"}",
),
],
),
),
],
),
);
},
),
);
},
),
])
],
theme: ExpandableListTheme.custom(
@ -517,7 +513,7 @@ class _FamilyCardsState extends State<FamilyCards> {
},
text: isActive ? LocaleKeys.active.tr(context: context) : LocaleKeys.switchLogin.tr(context: context),
backgroundColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor,
borderColor: isActive || !canSwitch ? Colors.grey.shade200 : AppColors.secondaryLightRedColor,
borderColor: Colors.transparent,
textColor: isActive || !canSwitch ? AppColors.greyTextColor : AppColors.primaryRedColor,
fontSize: 13.h,
icon: isActive ? AppAssets.activeCheck : AppAssets.switch_user,
@ -527,13 +523,15 @@ class _FamilyCardsState extends State<FamilyCards> {
],
),
if (widget.isShowRemoveButton) ...[
isParentUser ? Positioned(
top: 0,
right: 0,
child: Utils.buildSvgWithAssets(icon: AppAssets.deleteIcon).onPress(() {
if (!isActive) widget.onRemove(profile);
}),
) : SizedBox(),
isParentUser
? Positioned(
top: 0,
right: 0,
child: Utils.buildSvgWithAssets(icon: AppAssets.deleteIcon).onPress(() {
if (!isActive) widget.onRemove(profile);
}),
)
: SizedBox(),
],
],
),

@ -122,8 +122,8 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
children: [
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.secondaryLightRedColor,
initialIndex: 0,
tabs: [
CustomTabBarModel(null, LocaleKeys.visitsOrders.tr(context: context)),
@ -149,13 +149,11 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
myInvoicesViewModel.filterInvoices(InvoiceFilterType.all);
},
backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.all
? AppColors.primaryRedColor
: AppColors.textColor.withValues(alpha: 0.2),
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: myInvoicesVM.currentFilter == InvoiceFilterType.all ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontSize: 12.f,
fontWeight: FontWeight.w600,
borderRadius: 10,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
@ -166,15 +164,12 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
if (myInvoicesVM.getOriginalInvoicesList().isEmpty) return;
_showHospitalFilterBottomSheet(context);
},
backgroundColor:
myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital
? AppColors.primaryRedColor
: AppColors.textColor.withValues(alpha: 0.2),
backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: myInvoicesVM.currentFilter == InvoiceFilterType.hospital ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontSize: 12.f,
fontWeight: FontWeight.w600,
borderRadius: 10,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
@ -185,15 +180,12 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
if (myInvoicesVM.getOriginalInvoicesList().isEmpty) return;
_showClinicFilterBottomSheet(context);
},
backgroundColor:
myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic
? AppColors.primaryRedColor
: AppColors.textColor.withValues(alpha: 0.2),
backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: myInvoicesVM.currentFilter == InvoiceFilterType.clinic ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontSize: 12.f,
fontWeight: FontWeight.w600,
borderRadius: 10,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
@ -204,15 +196,12 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
if (myInvoicesVM.getOriginalInvoicesList().isEmpty) return;
_showDoctorFilterBottomSheet(context);
},
backgroundColor:
myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor
? AppColors.primaryRedColor
: AppColors.textColor.withValues(alpha: 0.2),
backgroundColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.bgRedLightColor : AppColors.whiteColor,
borderColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
textColor: myInvoicesVM.currentFilter == InvoiceFilterType.doctor ? AppColors.primaryRedColor : AppColors.blackColor,
fontSize: 12,
fontSize: 12.f,
fontWeight: FontWeight.w600,
borderRadius: 10,
borderRadius: 10.r,
padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
height: 40.h,
),
@ -245,16 +234,14 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
getInvoicesListResponseModel: myInvoicesVM.allInvoicesList[index],
onTap: () async {
if (Utils.isVidaPlusProject(myInvoicesVM.allInvoicesList[index].projectId ?? 0)) {
LoaderBottomSheet.showLoader(
loadingText: LocaleKeys.sendingEmailPleaseWait.tr(context: context));
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.sendingEmailPleaseWait.tr(context: context));
await myInvoicesViewModel.sendInvoiceEmail(
appointmentNo: myInvoicesVM.allInvoicesList[index].appointmentNo!,
projectID: myInvoicesVM.allInvoicesList[index].projectId!,
onSuccess: (val) {
LoaderBottomSheet.hideLoader();
showCommonBottomSheetWithoutHeight(context,
child: Utils.getSuccessWidget(
loadingText: LocaleKeys.emailSentSuccessfullyMessage.tr(context: context)),
child: Utils.getSuccessWidget(loadingText: LocaleKeys.emailSentSuccessfullyMessage.tr(context: context)),
callBackFunc: () {},
isFullScreen: false,
isCloseButtonVisible: true,
@ -289,8 +276,7 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
onSuccess: (value) async {
LoaderBottomSheet.hideLoader();
if (myInvoicesViewModel.downloadInvoicePDFBase64!.isNotEmpty) {
String path = await Utils.createFileFromString(
myInvoicesViewModel.downloadInvoicePDFBase64!, "pdf");
String path = await Utils.createFileFromString(myInvoicesViewModel.downloadInvoicePDFBase64!, "pdf");
try {
OpenFilex.open(path);
} catch (ex) {
@ -377,8 +363,7 @@ class _MyInvoicesListState extends State<MyInvoicesList> {
onSuccess: (value) async {
LoaderBottomSheet.hideLoader();
if (myInvoicesViewModel.downloadPharmacyInvoicePDFBase64!.isNotEmpty) {
String path = await Utils.createFileFromString(
myInvoicesViewModel.downloadPharmacyInvoicePDFBase64!, "pdf");
String path = await Utils.createFileFromString(myInvoicesViewModel.downloadPharmacyInvoicePDFBase64!, "pdf");
try {
OpenFilex.open(path);
} catch (ex) {

@ -111,7 +111,7 @@ class _OnboardingScreenState extends State<OnboardingScreen> {
onPressed: () => goToHomePage(),
width: 86.w,
height: 56.h,
backgroundColor: Color(0xffFEE9EA),
backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
borderColor: Colors.transparent,
).paddingOnly(left: 24.w)

@ -164,7 +164,7 @@ class _AnimatedScreenState extends State<AnimatedScreen> with TickerProviderStat
width: 400.h,
height: 653.h,
decoration: BoxDecoration(
color: Color(0xffED1C2B),
color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(330.h),
),
),
@ -273,7 +273,7 @@ class _MoveObjectDemoState extends State<MoveObjectDemo> with SingleTickerProvid
width: 200,
height: 375,
decoration: BoxDecoration(
color: const Color(0xffED1C2B),
color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(330),
),
),
@ -367,7 +367,7 @@ class _MoveOnClickDemoState extends State<MoveOnClickDemo> with TickerProviderSt
width: 100,
height: 150,
decoration: BoxDecoration(
color: const Color(0xffED1C2B),
color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(330),
),
),

@ -177,8 +177,8 @@ class PrescriptionItemView extends StatelessWidget {
// child: CustomButton(
// text: LocaleKeys.checkAvailability.tr(context: context),
// onPressed: () {},
// backgroundColor: AppColors.primaryRedColor.withOpacity(0.1),
// borderColor: AppColors.primaryRedColor.withOpacity(0.0),
// backgroundColor: AppColors.primaryRedColorBase.withOpacity(0.1),
// borderColor: AppColors.primaryRedColorBase.withOpacity(0.0),
// textColor: AppColors.primaryRedColor,
// fontSize: 14.f,
// isBold: true,

@ -52,14 +52,13 @@ class ProfileSettings extends StatefulWidget {
}
class ProfileSettingsState extends State<ProfileSettings> {
final InAppReview inAppReview = InAppReview.instance;
@override
void initState() {
super.initState();
_loadPermissions();
// Load data after first frame
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
@ -434,7 +433,15 @@ class ProfileSettingsState extends State<ProfileSettings> {
if (trailingLabel.isNotEmpty)
ConstrainedBox(
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * 0.35),
child: isEnglishOnly ? Directionality(textDirection: ui.TextDirection.ltr, child: trailingLabel.toText14(color: AppColors.greyTextColor, isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, isEnglishOnly: isEnglishOnly).toShimmer2(isShow: isShowLoading)) : trailingLabel.toText14(color: AppColors.greyTextColor, isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, isEnglishOnly: isEnglishOnly).toShimmer2(isShow: isShowLoading),
child: isEnglishOnly
? Directionality(
textDirection: ui.TextDirection.ltr,
child: trailingLabel
.toText14(color: AppColors.greyTextColor, isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, isEnglishOnly: isEnglishOnly)
.toShimmer2(isShow: isShowLoading))
: trailingLabel
.toText14(color: AppColors.greyTextColor, isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1, isEnglishOnly: isEnglishOnly)
.toShimmer2(isShow: isShowLoading),
),
switchValue != null
? Switch(
@ -452,8 +459,10 @@ class ProfileSettingsState extends State<ProfileSettings> {
);
}
Widget socialMedia(String icon, String label,
) {
Widget socialMedia(
String icon,
String label,
) {
return SizedBox(
height: 56.h,
child: Row(
@ -461,14 +470,13 @@ class ProfileSettingsState extends State<ProfileSettings> {
children: [
Utils.buildSvgWithAssets(icon: icon, iconColor: AppColors.greyTextColor),
label.toText14(isBold: true, textOverflow: TextOverflow.ellipsis, maxlines: 1).expanded,
Row(
spacing: 24.w,
children: [
Utils.buildSvgWithAssets(icon: AppAssets.x2).onPress(()=> launchUrl(Uri.parse("https://twitter.com/HMG"))),
Utils.buildSvgWithAssets(icon: AppAssets.fb).onPress(()=> launchUrl(Uri.parse("https://www.facebook.com/DrSulaimanAlHabib?ref=tn_tnmn"))),
Utils.buildSvgWithAssets(icon: AppAssets.linkedin).onPress(()=> launchUrl(Uri.parse("https://www.linkedin.com/company/drsulaiman-alhabib-medical-group"))),
Utils.buildSvgWithAssets(icon: AppAssets.youtube).onPress(()=> launchUrl(Uri.parse("https://www.youtube.com/c/DrsulaimanAlhabibHospitals"))),
Utils.buildSvgWithAssets(icon: AppAssets.x2).onPress(() => launchUrl(Uri.parse("https://twitter.com/HMG"))),
Utils.buildSvgWithAssets(icon: AppAssets.fb).onPress(() => launchUrl(Uri.parse("https://www.facebook.com/DrSulaimanAlHabib?ref=tn_tnmn"))),
Utils.buildSvgWithAssets(icon: AppAssets.linkedin).onPress(() => launchUrl(Uri.parse("https://www.linkedin.com/company/drsulaiman-alhabib-medical-group"))),
Utils.buildSvgWithAssets(icon: AppAssets.youtube).onPress(() => launchUrl(Uri.parse("https://www.youtube.com/c/DrsulaimanAlhabibHospitals"))),
],
)
],
@ -546,9 +554,7 @@ class _FamilyCardWidgetState extends State<FamilyCardWidget> {
// final isParentUser = appState.getAuthenticatedUser()?.isParentUser ?? false;
// final canSwitch = isParentUser || (!isParentUser && widget.profile.responseId == appState.getSuperUserID);
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor,
borderRadius: 24.r, hasShadow: true, hasDenseShadow: true),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true, hasDenseShadow: true),
child: Column(
children: [
Column(
@ -607,12 +613,14 @@ class _FamilyCardWidgetState extends State<FamilyCardWidget> {
labelText: "${LocaleKeys.fileno.tr(context: context)}: ${widget.profile.responseId}",
iconSize: 12.w,
),
isActive ? AppCustomChipWidget(
icon: AppAssets.checkmark_icon,
labelText: LocaleKeys.verified.tr(context: context),
iconColor: AppColors.successColor,
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
) : SizedBox.shrink(),
isActive
? AppCustomChipWidget(
icon: AppAssets.checkmark_icon,
labelText: LocaleKeys.verified.tr(context: context),
iconColor: AppColors.successColor,
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 6.w),
)
: SizedBox.shrink(),
],
),
],
@ -639,73 +647,76 @@ class _FamilyCardWidgetState extends State<FamilyCardWidget> {
),
// isActive && appState.getAuthenticatedUser()!.bloodGroup != null
// ?
isActive ? AppCustomChipWidget(
icon: AppAssets.blood_icon,
isActive
? AppCustomChipWidget(
icon: AppAssets.blood_icon,
labelPadding: EdgeInsetsDirectional.only(start: -6.w, end: 8.w),
labelText: appState.getAuthenticatedUser()!.bloodGroup ?? "N/A",
iconColor: AppColors.primaryRedColor)
: SizedBox(),
labelText: appState.getAuthenticatedUser()!.bloodGroup ?? "N/A",
iconColor: AppColors.primaryRedColor)
: SizedBox(),
Consumer<InsuranceViewModel>(builder: (context, insuranceVM, child) {
return isActive
? AppCustomChipWidget(
icon: insuranceVM.isInsuranceExpired
? AppAssets.cancel_circle_icon
: insuranceVM.isInsuranceActive
? AppAssets.insurance_active_icon
: AppAssets.alertSquare,
labelText: insuranceVM.isInsuranceExpired
? LocaleKeys.insuranceExpired.tr(context: context)
: insuranceVM.isInsuranceActive
? LocaleKeys.insuranceActive.tr(context: context)
: LocaleKeys.insuranceInActive.tr(context: context),
iconColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor
: insuranceVM.isInsuranceActive
? AppColors.successColor
: AppColors.warningColorYellow,
textColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor
: insuranceVM.isInsuranceActive
? AppColors.successColor
: AppColors.warningColorYellow,
iconSize: 12.w,
deleteIcon: insuranceVM.isInsuranceActive ? null : AppAssets.forward_chevron_icon,
deleteIconColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor
: insuranceVM.isInsuranceActive
? AppColors.successColor
: AppColors.warningColorYellow,
deleteIconHasColor: true,
onChipTap: () {
if (!insuranceVM.isInsuranceActive) {
insuranceVM.setIsInsuranceUpdateDetailsLoading(true);
insuranceVM.getPatientInsuranceDetailsForUpdate(appState.getAuthenticatedUser()!.patientId.toString(), appState.getAuthenticatedUser()!.patientIdentificationNo.toString());
showCommonBottomSheetWithoutHeight(context, child: PatientInsuranceCardUpdateCard(), callBackFunc: () {}, title: "", isCloseButtonVisible: false, isFullScreen: false);
// showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
// context,
// child: Utils.getWarningWidget(
// loadingText: LocaleKeys.insuranceInActiveContactSupport.tr(context: context),
// confirmText: LocaleKeys.contactUs.tr(context: context),
// isShowActionButtons: true,
// onCancelTap: () {
// Navigator.pop(context);
// },
// onConfirmTap: () async {
// launchUrl(Uri.parse("tel://" + "+966 92 006 6666"));
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
}
},
backgroundColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor.withValues(alpha: 0.1)
: insuranceVM.isInsuranceActive
? AppColors.successColor.withValues(alpha: 0.1)
: AppColors.warningColorYellow.withValues(alpha: 0.1),
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceVM.isInsuranceActive ? 6.w : 0.w),
? AppAssets.insurance_active_icon
: AppAssets.alertSquare,
labelText: insuranceVM.isInsuranceExpired
? LocaleKeys.insuranceExpired.tr(context: context)
: insuranceVM.isInsuranceActive
? LocaleKeys.insuranceActive.tr(context: context)
: LocaleKeys.insuranceInActive.tr(context: context),
iconColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor
: insuranceVM.isInsuranceActive
? AppColors.successColor
: AppColors.warningColorYellow,
textColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor
: insuranceVM.isInsuranceActive
? AppColors.successColor
: AppColors.warningColorYellow,
iconSize: 12.w,
deleteIcon: insuranceVM.isInsuranceActive ? null : AppAssets.forward_chevron_icon,
deleteIconColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor
: insuranceVM.isInsuranceActive
? AppColors.successColor
: AppColors.warningColorYellow,
deleteIconHasColor: true,
onChipTap: () {
if (!insuranceVM.isInsuranceActive) {
insuranceVM.setIsInsuranceUpdateDetailsLoading(true);
insuranceVM.getPatientInsuranceDetailsForUpdate(
appState.getAuthenticatedUser()!.patientId.toString(), appState.getAuthenticatedUser()!.patientIdentificationNo.toString());
showCommonBottomSheetWithoutHeight(context,
child: PatientInsuranceCardUpdateCard(), callBackFunc: () {}, title: "", isCloseButtonVisible: false, isFullScreen: false);
// showCommonBottomSheetWithoutHeight(
// title: LocaleKeys.notice.tr(context: context),
// context,
// child: Utils.getWarningWidget(
// loadingText: LocaleKeys.insuranceInActiveContactSupport.tr(context: context),
// confirmText: LocaleKeys.contactUs.tr(context: context),
// isShowActionButtons: true,
// onCancelTap: () {
// Navigator.pop(context);
// },
// onConfirmTap: () async {
// launchUrl(Uri.parse("tel://" + "+966 92 006 6666"));
// }),
// callBackFunc: () {},
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
}
},
backgroundColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor.withValues(alpha: 0.1)
: insuranceVM.isInsuranceActive
? AppColors.successColor.withValues(alpha: 0.1)
: AppColors.warningColorYellow.withValues(alpha: 0.1),
labelPadding: EdgeInsetsDirectional.only(start: -4.w, end: insuranceVM.isInsuranceActive ? 6.w : 0.w),
).toShimmer2(isShow: insuranceVM.isInsuranceLoading)
: SizedBox.shrink();
}),
@ -747,7 +758,7 @@ class _FamilyCardWidgetState extends State<FamilyCardWidget> {
height: isFoldable ? 50.h : 45.h,
fontSize: 14.f,
).paddingOnly(top: 12.h, right: 16.w, left: 16.w, bottom: 16.h);
}
}
Widget _buildNonParentUserButton(int? superUserId) {
final canSwitchBack = superUserId != null && superUserId == widget.profile.responseId;

@ -159,9 +159,9 @@ class _RateAppointmentClinicState extends State<RateAppointmentClinic> {
Expanded(
child: CustomButton(
text: LocaleKeys.back.tr(context: context),
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
onPressed: () {
appointmentRatingViewModel!.setTitle(LocaleKeys.rateDoctor.tr(context: context));
appointmentRatingViewModel!.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context));

@ -181,9 +181,9 @@ class _RateAppointmentDoctorState extends State<RateAppointmentDoctor> {
Expanded(
child: CustomButton(
text: LocaleKeys.later.tr(context: context),
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
onPressed: () {
Navigator.pop(context);
},

@ -67,8 +67,8 @@ class _ActivityDetailsState extends State<ActivityDetails> {
children: [
Expanded(
child: CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.secondaryLightRedColor,
tabs: [
CustomTabBarModel(null, "D"),
CustomTabBarModel(null, "W"),

@ -56,8 +56,8 @@ class _HealthDashboardState extends State<HealthDashboard> with SingleTickerProv
children: [
SizedBox(height: 96.h),
CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.secondaryLightRedColor,
tabs: [
CustomTabBarModel(null, "Overview".needTranslation),
CustomTabBarModel(null, "Details".needTranslation),

@ -480,7 +480,7 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> with SingleTicker
backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
deleteIcon: AppAssets.cancel,
deleteIconColor: AppColors.primaryRedColor,
deleteIconColor: AppColors.criticalActionColor,
deleteIconHasColor: false,
onDeleteTap: () {
viewModel.removeOrgan(organ.id);
@ -500,6 +500,7 @@ class _OrganSelectorPageState extends State<OrganSelectorPage> with SingleTicker
isDisabled: viewModel.selectedOrgans.isEmpty,
backgroundColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
borderColor: Colors.transparent,
),
);
}

@ -121,7 +121,7 @@ class _SelectedOrgansSectionState extends State<SelectedOrgansSection> {
backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
deleteIcon: AppAssets.cross_circle,
deleteIconColor: AppColors.primaryRedColor,
deleteIconColor: AppColors.criticalActionColor,
deleteIconHasColor: true,
onDeleteTap: () => widget.onRemoveOrgan(organ.id),
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),

@ -94,7 +94,7 @@ class _TriageLevelCardState extends State<TriageLevelCard> with SingleTickerProv
switch (triageLevel) {
case 'emergency_ambulance':
case 'emergency':
return AppColors.chipColorEmergency.withValues(alpha: 0.1);
return AppColors.primaryRedColor.withValues(alpha: 0.1);
case 'consultation_24':
case 'consultation':
return AppColors.chipColorSeekMedicalAdvice.withValues(alpha: 0.1);

@ -257,9 +257,9 @@ class AncillaryOrderCard extends StatelessWidget {
onViewDetails!();
}
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
fontWeight: FontWeight.w600,
borderRadius: 10.r,

@ -252,9 +252,9 @@ class AncillaryOrderCard extends StatelessWidget {
onViewDetails!();
}
},
backgroundColor: Color(0xffFEE9EA),
borderColor: Color(0xffFEE9EA),
textColor: Color(0xffED1C2B),
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
isBold: true,
borderRadius: 10.r,

@ -91,7 +91,7 @@ class SwitchCupBottomSheet extends StatelessWidget {
width: 60.w,
height: 60.w,
decoration: BoxDecoration(
color: isSelected ? AppColors.primaryRedColor.withOpacity(0.08) : Colors.transparent,
color: isSelected ? AppColors.secondaryLightRedColor : Colors.transparent,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isSelected ? AppColors.primaryRedColor : AppColors.bgScaffoldColor,
@ -271,9 +271,9 @@ class _CustomizeCupBottomSheetState extends State<CustomizeCupBottomSheet> {
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: AppColors.primaryRedColor,
inactiveTrackColor: AppColors.primaryRedColor.withOpacity(0.2),
inactiveTrackColor: AppColors.secondaryLightRedColor,
thumbColor: AppColors.primaryRedColor,
overlayColor: AppColors.primaryRedColor.withOpacity(0.2),
overlayColor: AppColors.secondaryLightRedColor,
trackHeight: 4.h,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10.w),
),

@ -62,7 +62,7 @@ class _WeatherDetailsPageState extends State<WeatherDetailsPage> {
case ColorName.ORANGE:
return AppColors.warningLightColor.withValues(alpha: 0.1);
case ColorName.RED:
return AppColors.errorLightColor.withValues(alpha: 0.1);
return AppColors.primaryRedColor.withValues(alpha: 0.1);
}
}
@ -120,7 +120,7 @@ class _WeatherDetailsPageState extends State<WeatherDetailsPage> {
}
// Red: Very High, At High Risk
else if (category.contains('very high') || category.contains('at high risk')) {
return AppColors.errorLightColor.withValues(alpha: 0.1);
return AppColors.primaryRedColor.withValues(alpha: 0.1);
}
// Orange: Risk, High (but not "very high" or "at high risk")
else if (category.contains('risk') || category.contains('high')) {

@ -0,0 +1,83 @@
import 'dart:convert';
import 'package:firebase_remote_config/firebase_remote_config.dart';
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:flutter/services.dart' show rootBundle;
import 'package:hmg_patient_app_new/services/logger_service.dart';
/// Firebase Remote Config wrapper. Fully generic — the app never hardcodes
/// occasion-specific values here; it only knows about the `occasion_config`
/// remote key and reads whatever JSON is published on the Firebase console.
abstract class RemoteConfigService {
/// Must be called once at startup, before the first frame if possible.
Future<void> initialize();
/// Fetches the latest values from Firebase (respecting the configured
/// minimum fetch interval) and activates them.
Future<bool> fetchAndActivate();
/// Raw JSON string currently held for the `occasion_config` key
/// (bundled default until the first successful fetch/activate).
String get occasionConfigRawJson;
}
class RemoteConfigServiceImp implements RemoteConfigService {
static const String occasionConfigKey = 'occasion_config';
static const String _defaultConfigAssetPath = 'assets/json/default_occasion_config.json';
final LoggerService loggerService;
final FirebaseRemoteConfig _remoteConfig;
RemoteConfigServiceImp({
required this.loggerService,
FirebaseRemoteConfig? remoteConfig,
}) : _remoteConfig = remoteConfig ?? FirebaseRemoteConfig.instance;
@override
Future<void> initialize() async {
try {
await _remoteConfig.setConfigSettings(
RemoteConfigSettings(
fetchTimeout: const Duration(seconds: 10),
// In debug builds, always fetch fresh values so you can test
// Remote Config changes instantly without waiting/throttling.
// Release builds keep a sane 1-hour minimum interval.
minimumFetchInterval: kDebugMode ? Duration.zero : const Duration(hours: 1),
),
);
// Bundled fallback so behaviour is deterministic (and identical to the
// current app look) before the very first successful remote fetch.
final defaultJson = await rootBundle.loadString(_defaultConfigAssetPath);
await _remoteConfig.setDefaults(<String, dynamic>{occasionConfigKey: defaultJson});
} catch (ex) {
loggerService.logError('RemoteConfigService.initialize error: $ex');
}
}
@override
Future<bool> fetchAndActivate() async {
try {
return await _remoteConfig.fetchAndActivate();
} catch (ex) {
loggerService.logError('RemoteConfigService.fetchAndActivate error: $ex');
return false;
}
}
@override
String get occasionConfigRawJson {
try {
final value = _remoteConfig.getString(occasionConfigKey);
if (value.isEmpty) return _fallbackDisabledJson;
// Validate JSON shape early; if malformed, fall back safely.
json.decode(value);
return value;
} catch (ex) {
loggerService.logError('RemoteConfigService.occasionConfigRawJson parse error: $ex');
return _fallbackDisabledJson;
}
}
static const String _fallbackDisabledJson = '{"key":"none","enabled":false}';
}

@ -5,41 +5,115 @@ class AppColors {
/// When true, every getter below returns the dark palette value.
static bool isDarkMode = false;
/// Global occasion-theme flag — toggled by OccasionThemeViewModel based on
/// Firebase Remote Config. When false (the default), the app looks exactly
/// like it does today — no colors are affected.
static bool isOccasionThemeActive = false;
/// Remotely-configured primary color for the currently active occasion
/// (e.g. National Day). Null unless an occasion theme is active.
static Color? occasionPrimaryColor;
/// Lighter/translucent (11% opacity) variant of [occasionPrimaryColor],
/// derived automatically by OccasionThemeViewModel. Null unless an
/// occasion theme is active.
static Color? occasionSecondaryColor;
static const transparent = Colors.transparent;
// ── Scaffold / Background ─────────────────────────────────────────────────
static Color get scaffoldBgColor => isDarkMode ? dark.scaffoldBgColor : const Color(0xFFF8F8F8);
static Color get bottomSheetBgColor => isDarkMode ? dark.bottomSheetBgColor : const Color(0xFFF8F8FA);
static Color get lightGreyEFColor => isDarkMode ? dark.lightGreyEFColor : const Color(0xffeaeaff);
static Color get greyF7Color => isDarkMode ? dark.greyF7Color : const Color(0xffF7F7F7);
static Color get greyInfoTextColor => isDarkMode ? dark.greyInfoTextColor : const Color(0xff777777);
static Color get lightGrayColor => isDarkMode ? dark.lightGrayColor : const Color(0xff808080);
static Color get greyTextColorLight => isDarkMode ? dark.greyTextColorLight : const Color(0xFFA2A2A2);
static Color get mainPurple => isDarkMode ? dark.mainPurple : const Color(0xFF7954F7);
// ── Core Colors ───────────────────────────────────────────────────────────
static Color get whiteColor => isDarkMode ? dark.whiteColor : const Color(0xFFffffff);
// static const Color primaryRedColor = Color(0xFFED1C2B);
static Color get primaryRedColor => isDarkMode ? dark.primaryRedColor : Color(0xFFED1C2B);
static Color get secondaryLightRedColor => isDarkMode ? dark.secondaryLightRedColor : const Color(0xFFFEE9EA);
/// Occasion-aware primary brand color. Resolves to the remotely-configured
/// occasion color when active (e.g. National Day green), otherwise falls
/// back to the original dark/light red — identical to current behavior.
///
/// NOTE: Do NOT use this getter for `.withOpacity()/.withValues()` derived
/// (translucent) colors — use [primaryRedColorBase] for those instead, so
/// occasion theming never affects translucent overlays/backgrounds.
static Color get primaryRedColor => (isOccasionThemeActive && occasionPrimaryColor != null) ? occasionPrimaryColor! : primaryRedColorBase;
/// The original, non-themed brand red (or its dark-mode variant). Always
/// stable regardless of any active occasion theme — intended for opacity
/// derived usages (e.g. `AppColors.primaryRedColorBase.withOpacity(0.1)`).
static Color get primaryRedColorBase => isDarkMode ? dark.primaryRedColor : const Color(0xFFED1C2B);
/// Semantic color for critical/negative actions (delete/deactivate account,
/// cancel appointment, clear/reset, emergency, etc.). Same light/dark
/// values as [primaryRedColorBase] — respects dark mode, but is never
/// affected by the active occasion/remote-config theme, so destructive
/// actions always read as "red" regardless of any occasion branding.
static Color get criticalActionColor => primaryRedColorBase;
/// Occasion-aware light/tint variant of [primaryRedColor] (e.g. chip and
/// badge backgrounds). Resolves to the remotely-configured occasion's
/// secondary color when active (e.g. National Day's light green),
/// otherwise falls back to the original dark/light tint — identical to
/// current behavior.
///
/// NOTE: Do NOT use this getter for `.withOpacity()/.withValues()` derived
/// colors — use [secondaryLightRedColorBase] for those instead, so
/// occasion theming never affects translucent overlays/backgrounds.
static Color get secondaryLightRedColor => (isOccasionThemeActive && occasionSecondaryColor != null) ? occasionSecondaryColor! : secondaryLightRedColorBase;
/// The original, non-themed light-red tint (or its dark-mode variant).
/// Always stable regardless of any active occasion theme — intended for
/// opacity derived usages.
static Color get secondaryLightRedColorBase => isDarkMode ? dark.secondaryLightRedColor : const Color(0xFFFEE9EA);
static const Color successColor = Color(0xFF18C273);
static Color get successLightBgColor => isDarkMode ? dark.successLightBgColor : const Color(0xffDDF6EA);
static Color get greyColor => isDarkMode ? dark.greyColor : const Color(0xFFEFEFF0);
static Color get chipBgColor => isDarkMode ? dark.chipBgColor : const Color(0xFFEFEFF0);
static Color get textColor => isDarkMode ? dark.textColor : const Color(0xFF2E3039);
static Color get iconColor => isDarkMode ? dark.iconColor : const Color(0xFF2E3039);
static Color get borderGrayColor => isDarkMode ? dark.borderGrayColor : const Color(0x332E3039);
static Color get textColorLight => isDarkMode ? dark.textColorLight : const Color(0xFF5E5E5E);
static Color get dividerColor => isDarkMode ? dark.dividerColor : const Color(0x40D2D2D2);
static Color get warningColorYellow => isDarkMode ? dark.warningColorYellow : const Color(0xFFF4A308);
static Color get inputLabelTextColor => isDarkMode ? dark.inputLabelTextColor : const Color(0xff898A8D);
static Color get greyTextColor => isDarkMode ? dark.greyTextColor : const Color(0xFF8F9AA3);
static Color get lightGrayBGColor => isDarkMode ? dark.lightGrayBGColor : const Color(0x142E3039);
static Color get checkBoxBorderColor => isDarkMode ? dark.checkBoxBorderColor : const Color(0xffD2D2D2);
static Color get pharmacyBGColor => isDarkMode ? dark.pharmacyBGColor : const Color(0xFF359846);
static Color get lightGreenColor => isDarkMode ? dark.lightGreenColor : const Color(0xFF0ccedde);
static Color get ratingColorYellow => isDarkMode ? dark.ratingColorYellow : const Color(0xFFFFAF15);
static Color get spacerLineColor => isDarkMode ? dark.spacerLineColor : const Color(0x2E30391A);
static Color get habibPharmacyColor => const Color(0xFF2d9137);
// ── Doctor Rating Colors ──────────────────────────────────────────────────
@ -52,92 +126,147 @@ class AppColors {
static const Color ratingBadgeFiveStarColor = Color(0xFF5BBD8E); // Green badge for ≥4.0
static const Color ratingPercentageTextColor = Color(0xFF4A4A4A); // Dark grey for percentage text
// ── Semantic / Status Colors ──────────────────────────────────────────────
static Color get errorColor => isDarkMode ? dark.errorColor : const Color(0xFFED1C2B);
static Color get alertColor => isDarkMode ? dark.alertColor : const Color(0xFFD48D05);
static Color get infoColor => isDarkMode ? dark.infoColor : const Color(0xFF0B85F7);
static Color get warningColor => isDarkMode ? dark.warningColor : const Color(0xFFFFCC00);
static Color get switchBackgroundColor => isDarkMode ? dark.switchBackgroundColor : const Color(0x2618C273);
static Color get bottomNAVBorder => isDarkMode ? dark.bottomNAVBorder : const Color(0xFFEEEEEE);
static Color get quickLoginColor => isDarkMode ? dark.quickLoginColor : const Color(0xFF666666);
static Color get tooltipTextColor => isDarkMode ? dark.tooltipTextColor : const Color(0xFF414D55);
static Color get graphGridColor => isDarkMode ? dark.graphGridColor : const Color(0x4D18C273);
static Color get highAndLow => isDarkMode ? dark.highAndLow : const Color(0xFFFFAF15);
static Color get labelTextColor => isDarkMode ? dark.labelTextColor : const Color(0xFF838383);
static Color get calenderTextColor => isDarkMode ? dark.calenderTextColor : const Color(0xFFD0D0D0);
static Color get lightGreenButtonColor => isDarkMode ? dark.lightGreenButtonColor : const Color(0x2618C273);
static Color get lightRedButtonColor => isDarkMode ? dark.lightRedButtonColor : const Color(0x1AED1C2B);
// ── Status Colors ─────────────────────────────────────────────────────────
static Color get statusPendingColor => isDarkMode ? dark.statusPendingColor : const Color(0xffCC9B14);
static Color get statusProcessingColor => isDarkMode ? dark.statusProcessingColor : const Color(0xff2E303A);
static Color get statusCompletedColor => isDarkMode ? dark.statusCompletedColor : const Color(0xff359846);
static Color get statusRejectedColor => isDarkMode ? dark.statusRejectedColor : const Color(0xffD02127);
// ── Info Banner Colors ────────────────────────────────────────────────────
static Color get infoBannerBgColor => isDarkMode ? dark.infoBannerBgColor : const Color(0xFFFFF4E6);
static Color get infoBannerBorderColor => isDarkMode ? dark.infoBannerBorderColor : const Color(0xFFFFE5B4);
static Color get infoBannerIconColor => isDarkMode ? dark.infoBannerIconColor : const Color(0xFFCC9B14);
static Color get infoBannerTextColor => isDarkMode ? dark.infoBannerTextColor : const Color(0xFF856404);
// ── Symptoms Checker ─────────────────────────────────────────────<EFBFBD><EFBFBD>────────
static Color get chipColorSeekMedicalAdvice => isDarkMode ? dark.chipColorSeekMedicalAdvice : const Color(0xFFFFAF15);
static Color get chipTextColorSeekMedicalAdvice => isDarkMode ? dark.chipTextColorSeekMedicalAdvice : const Color(0xFFD48D05);
// ── Services Page Colors ──────────────────────────────────────────────────
static Color get eReferralCardColor => isDarkMode ? dark.eReferralCardColor : const Color(0xFFFF8012);
static Color get bloodDonationCardColor => isDarkMode ? dark.bloodDonationCardColor : const Color(0xFFFF5662);
static Color get myChildVaccineCardColor => isDarkMode ? dark.myChildVaccineCardColor : const Color(0xFFFF2D78);
static Color get bookAppointment => isDarkMode ? dark.bookAppointment : const Color(0xFF415364);
// ── Health Calculators Colors ─────────────────────────────────────────────
static Color get bmiCalculatorColor => isDarkMode ? dark.bmiCalculatorColor : const Color(0xFFF43333);
static Color get caloriesCalculatorColor => isDarkMode ? dark.caloriesCalculatorColor : const Color(0xFF0B85F7);
static Color get bmrCalculatorColor => isDarkMode ? dark.bmrCalculatorColor : const Color(0xFF18C273);
static Color get idealBodyWeightColor => isDarkMode ? dark.idealBodyWeightColor : const Color(0xFFFF8012);
static Color get bodyFatCalculatorColor => isDarkMode ? dark.bodyFatCalculatorColor : const Color(0xFFFF5662);
static Color get carbsProteinFatColor => isDarkMode ? dark.carbsProteinFatColor : const Color(0xFF18C273);
static Color get ovulationPeriodColor => isDarkMode ? dark.ovulationPeriodColor : const Color(0xFFFF47A9);
static Color get deliveryDueDateColor => isDarkMode ? dark.deliveryDueDateColor : const Color(0xFFFF2D78);
// ── Water Monitor ─────────────────────────────────────────────────────────
static Color get blueColor => isDarkMode ? dark.blueColor : const Color(0xFF4EB5FF);
static Color get blueGradientColorOne => isDarkMode ? dark.blueGradientColorOne : const Color(0xFFF1F7FD);
static Color get blueGradientColorTwo => isDarkMode ? dark.blueGradientColorTwo : const Color(0xFFD9EFFF);
// ── Shimmer ───────────────────────────────────────────────────────────────
static Color get shimmerBaseColor => isDarkMode ? dark.shimmerBaseColor : const Color(0xFFE0E0E0);
static Color get shimmerHighlightColor => isDarkMode ? dark.shimmerHighlightColor : const Color(0xFFF5F5F5);
static Color get covid29Color => isDarkMode ? dark.covid29Color : const Color(0xff2563EB);
static Color get lightGreyTextColor => isDarkMode ? dark.lightGreyTextColor : const Color(0xFF959595);
static Color get labelColorYellow => isDarkMode ? dark.labelColorYellow : const Color(0xFFFBCB6E);
static const LinearGradient aiLinearGradient =
LinearGradient(colors: [Color(0xFF8A38F5), Color(0xFFE20BBB)], begin: Alignment.topLeft, end: Alignment.bottomRight);
static const LinearGradient aiLinearGradient = LinearGradient(colors: [Color(0xFF8A38F5), Color(0xFFE20BBB)], begin: Alignment.topLeft, end: Alignment.bottomRight);
// ── Aliases ───────────────────────────────────────────────────────────────
static Color get bgScaffoldColor => scaffoldBgColor;
static Color get primaryRedBorderColor => primaryRedColor;
static Color get chipPrimaryRedBorderColor => primaryRedColor;
static Color get criticalLowAndHigh => primaryRedColor;
static Color get chipColorEmergency => primaryRedColor;
static Color get errorLightColor => primaryRedColor;
static Color get secondaryLightRedBorderColor => secondaryLightRedColor;
static Color get bgRedLightColor => secondaryLightRedColor;
static Color get chipSecondaryLightRedColor => secondaryLightRedColor;
static Color get blackBgColor => textColor;
static Color get blackColor => textColor;
static Color get borderOnlyColor => textColor;
static Color get chipBorderColorOpacity20 => borderGrayColor;
static Color get bgGreenColor => successColor;
static Color get textGreenColor => successColor;
static Color get successLightColor => successColor;
static Color get thumbColor => successColor;
static Color get chipColorMonitor => successColor;
static Color get greyLightColor => greyColor;
static Color get alertLightColor => alertColor;
static Color get infoLightColor => infoColor;
static Color get warningLightColor => warningColor;
// ── Dark Mode Palette ─────────────────────────────────────────────────────
@ -152,11 +281,17 @@ class AppColorsDark {
// Scaffold / Background
Color get scaffoldBgColor => const Color(0xFF191919);
Color get bottomSheetBgColor => const Color(0xFF1C1C1E);
Color get lightGreyEFColor => const Color(0xFF2A2A3A);
Color get greyF7Color => const Color(0xFF1E1E1E);
Color get greyInfoTextColor => const Color(0xFF9E9E9E);
Color get lightGrayColor => const Color(0xFF9E9E9E);
Color get greyTextColorLight => const Color(0xFF757575);
// ⚠️ NEW — was missing from AppColorsDark
@ -164,114 +299,192 @@ class AppColorsDark {
// Core
Color get baseColor => const Color(0xFF1E1E1E);
// ⚠️ NEW — maps whiteColor → dark surface color
Color get whiteColor => const Color(0xFF1E1E1E); // surface replacement for white
Color get primaryRedColor => const Color(0xFFDE5C5D);
Color get secondaryLightRedColor => const Color(0xFF3A1015);
Color get successColor => const Color(0xFF18C273);
Color get successLightBgColor => const Color(0xFF0D2E1F);
Color get textColor => const Color(0xFFFFFFFF);
Color get iconColor => const Color(0xFFFFFFFF);
Color get borderGrayColor => const Color(0x55ECECEC);
Color get textColorLight => const Color(0xFFB0B0B0);
Color get dividerColor => const Color(0x33FFFFFF);
Color get warningColorYellow => const Color(0xFFF4A308);
Color get inputLabelTextColor => const Color(0xFF9E9E9E);
Color get greyTextColor => const Color(0xFF8F9AA3);
Color get lightGrayBGColor => const Color(0x22FFFFFF);
Color get checkBoxBorderColor => const Color(0xFF555555);
Color get pharmacyBGColor => const Color(0xFF359846);
// ⚠️ NEW — was missing from AppColorsDark
Color get lightGreenColor => const Color(0xFF0ccedde); // same teal accent — works on dark
Color get ratingColorYellow => const Color(0xFFFFAF15);
Color get spacerLineColor => const Color(0x22FFFFFF);
// Semantic / Status
Color get errorColor => const Color(0xFFD63D48);
Color get alertColor => const Color(0xFFD48D05);
Color get infoColor => const Color(0xFF4DA6FF);
Color get warningColor => const Color(0xFFFFCC00);
Color get greyColor => const Color(0xFF2C2C2E);
Color get chipBgColor => const Color(0xFF3E3E3E);
Color get cardDarkBorderColor => const Color(0x0DEAEAEA);
Color get serviceCardButtonColor => const Color(0xFF3E3E3E);
Color get switchBackgroundColor => const Color(0x2618C273);
Color get bottomNAVBorder => const Color(0xFF2C2C2E);
Color get quickLoginColor => const Color(0xFF9E9E9E);
Color get tooltipTextColor => const Color(0xFFCFD8DC);
Color get graphGridColor => const Color(0x4D18C273);
Color get highAndLow => const Color(0xFFFFAF15);
Color get labelTextColor => const Color(0xFF9E9E9E);
Color get calenderTextColor => const Color(0xFF555555);
Color get lightGreenButtonColor => const Color(0x2618C273);
Color get lightRedButtonColor => const Color(0x1AED1C2B);
// Status
Color get statusPendingColor => const Color(0xFFCC9B14);
Color get statusProcessingColor => const Color(0xFFECECEC);
Color get statusCompletedColor => const Color(0xFF359846);
Color get statusRejectedColor => const Color(0xFFD02127);
// Info Banner
Color get infoBannerBgColor => const Color(0xFF2A1F0A);
Color get infoBannerBorderColor => const Color(0xFF5A3D0A);
Color get infoBannerIconColor => const Color(0xFFCC9B14);
Color get infoBannerTextColor => const Color(0xFFFFCB6B);
// Symptoms Checker
Color get chipColorSeekMedicalAdvice => const Color(0xFFFFAF15);
Color get chipTextColorSeekMedicalAdvice => const Color(0xFFD48D05);
// Services Page
Color get eReferralCardColor => const Color(0xFFFF8012);
Color get bloodDonationCardColor => const Color(0xFFFF5662);
Color get myChildVaccineCardColor => const Color(0xFFFF2D78);
Color get bookAppointment => const Color(0xFF415364);
// Health Calculators
Color get bmiCalculatorColor => const Color(0xFFF43333);
Color get caloriesCalculatorColor => const Color(0xFF0B85F7);
Color get bmrCalculatorColor => const Color(0xFF18C273);
Color get idealBodyWeightColor => const Color(0xFFFF8012);
Color get bodyFatCalculatorColor => const Color(0xFFFF5662);
Color get carbsProteinFatColor => const Color(0xFF18C273);
Color get ovulationPeriodColor => const Color(0xFFFF47A9);
Color get deliveryDueDateColor => const Color(0xFFFF2D78);
// Water Monitor
Color get blueColor => const Color(0xFF4EB5FF);
Color get blueGradientColorOne => const Color(0xFF101C2A);
Color get blueGradientColorTwo => const Color(0xFF0D2235);
// Shimmer
Color get shimmerBaseColor => const Color(0xFF2C2C2C);
Color get shimmerHighlightColor => const Color(0xFF3D3D3D);
Color get covid29Color => const Color(0xFF2563EB);
Color get lightGreyTextColor => const Color(0xFF757575);
Color get labelColorYellow => const Color(0xFFFBCB6E);
// Aliases
Color get bgScaffoldColor => scaffoldBgColor;
Color get primaryRedBorderColor => primaryRedColor;
Color get chipPrimaryRedBorderColor => primaryRedColor;
Color get criticalLowAndHigh => primaryRedColor;
Color get chipColorEmergency => primaryRedColor;
Color get errorLightColor => primaryRedColor;
Color get secondaryLightRedBorderColor => secondaryLightRedColor;
Color get bgRedLightColor => secondaryLightRedColor;
Color get chipSecondaryLightRedColor => secondaryLightRedColor;
Color get blackBgColor => textColor;
Color get blackColor => textColor;
Color get borderOnlyColor => textColor;
Color get chipBorderColorOpacity20 => borderGrayColor;
Color get bgGreenColor => successColor;
Color get textGreenColor => successColor;
Color get successLightColor => successColor;
Color get thumbColor => successColor;
Color get chipColorMonitor => successColor;
Color get greyLightColor => greyColor;
Color get alertLightColor => alertColor;
Color get infoLightColor => infoColor;
Color get warningLightColor => warningColor;
}
@ -283,61 +496,99 @@ extension AppColorsContext on BuildContext {
// Scaffold / Background
Color get scaffoldBgColor => _isDark ? AppColors.dark.scaffoldBgColor : const Color(0xFFF8F8F8);
Color get bottomSheetBgColor => _isDark ? AppColors.dark.bottomSheetBgColor : const Color(0xFFF8F8FA);
Color get greyF7Color => _isDark ? AppColors.dark.greyF7Color : const Color(0xffF7F7F7);
Color get greyInfoTextColor => _isDark ? AppColors.dark.greyInfoTextColor : const Color(0xff777777);
Color get lightGrayColor => _isDark ? AppColors.dark.lightGrayColor : const Color(0xff808080);
// Core
Color get cardBaseColor => _isDark ? AppColors.dark.baseColor : const Color(0xFFFFFFFF);
Color get primaryRedColor => _isDark ? AppColors.dark.primaryRedColor : AppColors.primaryRedColor;
Color get secondaryLightRedColor => _isDark ? AppColors.dark.serviceCardButtonColor : const Color(0xFFFEE9EA);
Color get secondaryLightRedForegroundColor => _isDark ? const Color(0xFFFFFFFF) : AppColors.primaryRedColor;
Color get successColor => AppColors.successColor;
Color get successLightBgColor => _isDark ? AppColors.dark.successLightBgColor : const Color(0xffDDF6EA);
Color get textColor => _isDark ? AppColors.dark.textColor : const Color(0xFF2E3039);
Color get iconColor => _isDark ? AppColors.dark.iconColor : const Color(0xFF2E3039);
Color get borderGrayColor => _isDark ? AppColors.dark.borderGrayColor : const Color(0x332E3039);
Color get textColorLight => _isDark ? AppColors.dark.textColorLight : const Color(0xFF5E5E5E);
Color get dividerColor => _isDark ? AppColors.dark.dividerColor : const Color(0x40D2D2D2);
Color get inputLabelTextColor => _isDark ? AppColors.dark.inputLabelTextColor : const Color(0xff898A8D);
Color get greyTextColor => AppColors.greyTextColor;
Color get lightGrayBGColor => _isDark ? AppColors.dark.lightGrayBGColor : const Color(0x142E3039);
Color get checkBoxBorderColor => _isDark ? AppColors.dark.checkBoxBorderColor : const Color(0xffD2D2D2);
// Status / Semantic
Color get errorColor => _isDark ? AppColors.dark.errorColor : const Color(0xFFED1C2B);
Color get alertColor => AppColors.alertColor;
Color get infoColor => _isDark ? AppColors.dark.infoColor : const Color(0xFF0B85F7);
Color get warningColor => AppColors.warningColor;
Color get greyColor => _isDark ? AppColors.dark.greyColor : const Color(0xFFEFEFF0);
Color get chipBgColor => _isDark ? AppColors.dark.chipBgColor : const Color(0xFFEFEFF0);
Color get cardDarkBorderColor => _isDark ? AppColors.dark.cardDarkBorderColor : Colors.transparent;
Color get serviceCardButtonColor => _isDark ? AppColors.dark.serviceCardButtonColor : const Color(0xFF2E3039);
Color get bottomNAVBorder => _isDark ? AppColors.dark.bottomNAVBorder : const Color(0xFFEEEEEE);
Color get labelTextColor => _isDark ? AppColors.dark.labelTextColor : const Color(0xFF838383);
Color get lightGreyTextColor => _isDark ? AppColors.dark.lightGreyTextColor : const Color(0xFF959595);
// Info Banner
Color get infoBannerBgColor => _isDark ? AppColors.dark.infoBannerBgColor : const Color(0xFFFFF4E6);
Color get infoBannerBorderColor => _isDark ? AppColors.dark.infoBannerBorderColor : const Color(0xFFFFE5B4);
Color get infoBannerIconColor => AppColors.infoBannerIconColor;
Color get infoBannerTextColor => _isDark ? AppColors.dark.infoBannerTextColor : const Color(0xFF856404);
// Water Monitor
Color get blueColor => AppColors.blueColor;
Color get blueGradientColorOne => _isDark ? AppColors.dark.blueGradientColorOne : const Color(0xFFF1F7FD);
Color get blueGradientColorTwo => _isDark ? AppColors.dark.blueGradientColorTwo : const Color(0xFFD9EFFF);
// Shimmer
Color get shimmerBaseColor => _isDark ? AppColors.dark.shimmerBaseColor : const Color(0xFFE0E0E0);
Color get shimmerHighlightColor => _isDark ? AppColors.dark.shimmerHighlightColor : const Color(0xFFF5F5F5);
static const Color tooltipColor = Color(0xFF1AACACAC);
// Aliases
Color get bgScaffoldColor => scaffoldBgColor;
Color get bgGreenColor => successColor;
Color get bgRedLightColor => secondaryLightRedColor;
Color get blackColor => textColor;
Color get blackBgColor => textColor;
}

@ -115,8 +115,7 @@ class CollapsingListView extends StatelessWidget {
margin: EdgeInsets.fromLTRB(24.w, 0, 24.w, 0),
child: Transform.flip(
flipX: appState.isArabic(),
child: Utils.buildSvgWithAssets(
icon: isClose ? AppAssets.close_bottom_nav_trans : AppAssets.arrow_back_new, width: 24.h, height: 24.h),
child: Utils.buildSvgWithAssets(icon: isClose ? AppAssets.close_bottom_nav_trans : AppAssets.arrow_back_new, width: 24.h, height: 24.h),
),
).onPress(() {
if (leadingCallback != null) {
@ -289,8 +288,7 @@ class _ScrollAnimatedTitleState extends State<ScrollAnimatedTitle> {
return Container(
// height: (widget.preferredSize.height - _fontSize / 2).h,
height: 56.h,
alignment:
isRtl ? (widget.showBack ? Alignment.topRight : Alignment.centerRight) : (widget.showBack ? Alignment.topLeft : Alignment.centerLeft),
alignment: isRtl ? (widget.showBack ? Alignment.topRight : Alignment.centerRight) : (widget.showBack ? Alignment.topLeft : Alignment.centerLeft),
padding: EdgeInsets.fromLTRB(24.w, 0, 24.w, 0),
child: Row(
spacing: 4.h,
@ -306,38 +304,19 @@ class _ScrollAnimatedTitleState extends State<ScrollAnimatedTitle> {
),
).expanded,
...[
if (widget.logout != null)
actionButton(context, t, title: LocaleKeys.logout.tr(context: context), icon: AppAssets.logout).onPress(widget.logout!),
if (widget.report != null)
actionButton(context, t, title: LocaleKeys.feedback.tr(context: context), icon: AppAssets.report_icon).onPress(widget.report!),
if (widget.history != null)
actionButton(context, t, title: LocaleKeys.history.tr(context: context), icon: AppAssets.insurance_history_icon)
.onPress(widget.history!),
if (widget.instructions != null)
actionButton(context, t, title: LocaleKeys.instructions.tr(context: context), icon: AppAssets.requests).onPress(widget.instructions!),
if (widget.requests != null)
actionButton(context, t, title: LocaleKeys.requests.tr(context: context), icon: AppAssets.insurance_history_icon)
.onPress(widget.requests!),
if (widget.sendEmail != null)
actionButton(context, t, title: LocaleKeys.sendEmail.tr(context: context), icon: AppAssets.email).onPress(widget.sendEmail!),
if (widget.doctorResponse != null)
actionButton(context, t, title: LocaleKeys.doctorResponses.tr(context: context), icon: AppAssets.doctorResponseIcon)
.onPress(widget.doctorResponse!),
if (widget.logout != null) actionButton(context, t, title: LocaleKeys.logout.tr(context: context), icon: AppAssets.logout).onPress(widget.logout!),
if (widget.report != null) actionButton(context, t, title: LocaleKeys.feedback.tr(context: context), icon: AppAssets.report_icon).onPress(widget.report!),
if (widget.history != null) actionButton(context, t, title: LocaleKeys.history.tr(context: context), icon: AppAssets.insurance_history_icon).onPress(widget.history!),
if (widget.instructions != null) actionButton(context, t, title: LocaleKeys.instructions.tr(context: context), icon: AppAssets.requests).onPress(widget.instructions!),
if (widget.requests != null) actionButton(context, t, title: LocaleKeys.requests.tr(context: context), icon: AppAssets.insurance_history_icon).onPress(widget.requests!),
if (widget.sendEmail != null) actionButton(context, t, title: LocaleKeys.sendEmail.tr(context: context), icon: AppAssets.email).onPress(widget.sendEmail!),
if (widget.doctorResponse != null) actionButton(context, t, title: LocaleKeys.doctorResponses.tr(context: context), icon: AppAssets.doctorResponseIcon).onPress(widget.doctorResponse!),
if (widget.search != null) Utils.buildSvgWithAssets(icon: AppAssets.search_icon).onPress(widget.search!),
if (widget.aiOverview != null)
actionButton(context, t, title: LocaleKeys.aiOverView.tr(context: context), icon: AppAssets.aiOverView, isAiButton: true)
.onPress(widget.aiOverview!),
if (widget.downloadReport != null)
actionButton(context, t, title: LocaleKeys.downloadReport.tr(context: context), icon: AppAssets.download)
.onPress(widget.downloadReport!),
if (widget.viewImage != null)
actionButton(context, t, title: LocaleKeys.viewRadiologyImage.tr(context: context), icon: AppAssets.download)
.onPress(widget.viewImage!),
if (widget.location != null)
actionButton(context, t, title: LocaleKeys.sortByLocation.tr(context: context), icon: AppAssets.location).onPress(widget.location!),
if (widget.downloadInvoice != null)
actionButton(context, t, title: LocaleKeys.downloadInvoice.tr(context: context), icon: AppAssets.download)
.onPress(widget.downloadInvoice!),
if (widget.aiOverview != null) actionButton(context, t, title: LocaleKeys.aiOverView.tr(context: context), icon: AppAssets.aiOverView, isAiButton: true).onPress(widget.aiOverview!),
if (widget.downloadReport != null) actionButton(context, t, title: LocaleKeys.downloadReport.tr(context: context), icon: AppAssets.download).onPress(widget.downloadReport!),
if (widget.viewImage != null) actionButton(context, t, title: LocaleKeys.viewRadiologyImage.tr(context: context), icon: AppAssets.download).onPress(widget.viewImage!),
if (widget.location != null) actionButton(context, t, title: LocaleKeys.sortByLocation.tr(context: context), icon: AppAssets.location).onPress(widget.location!),
if (widget.downloadInvoice != null) actionButton(context, t, title: LocaleKeys.downloadInvoice.tr(context: context), icon: AppAssets.download).onPress(widget.downloadInvoice!),
if (widget.trailing != null) widget.trailing!,
]
],
@ -353,8 +332,7 @@ class _ScrollAnimatedTitleState extends State<ScrollAnimatedTitle> {
child: Container(
height: isFoldable ? 50.h : 40.h,
padding: EdgeInsets.all(8.w),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: AppColors.whiteColor, borderRadius: 8.r, side: BorderSide(width: 1, color: AppColors.borderGrayColor)),
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 8.r, side: BorderSide(width: 1, color: AppColors.borderGrayColor)),
child: ShaderMask(
blendMode: BlendMode.srcIn,
shaderCallback: (bounds) => AppColors.aiLinearGradient.createShader(bounds),

@ -28,7 +28,13 @@ class BottomNavigation extends StatelessWidget {
icon: AppAssets.bookAppoBottom,
fillIcon: AppAssets.bookAppoBottom,
label: LocaleKeys.appointments.tr(context: context),
iconSize: 32, isSpecial: true),
iconSize: 32,
isSpecial: true,
// Explicit dynamic color — the SVG asset itself is a fixed red, so
// without this override the Appointments icon would never react to
// dark mode or the active occasion theme (e.g. National Day green).
color: AppColors.primaryRedColor,
),
appState.isAuthenticated
// ? BottomNavItem(icon: AppAssets.toDoBottom, label: LocaleKeys.todoList.tr(context: context))
? BottomNavItem(icon: AppAssets.symptomCheckerBottomIcon, fillIcon: AppAssets.symptomCheckerBottomFillIcon, label: LocaleKeys.symptoms.tr(context: context))
@ -75,7 +81,8 @@ class BottomNavigation extends StatelessWidget {
icon: isSelected ? item.fillIcon : item.icon,
height: item.iconSize.h,
width: item.iconSize.w,
applyThemeColor: !item.isSpecial
applyThemeColor: !item.isSpecial,
iconColor: item.color,
// iconColor: isSelected ? Colors.black87 : Colors.black87,
),
// const SizedBox(height: 10),

@ -10,8 +10,8 @@ class CustomButton extends StatefulWidget {
final String text;
final String? icon;
final VoidCallback? onPressed;
final Color backgroundColor;
final Color borderColor;
final Color? backgroundColor;
final Color? borderColor;
final Color textColor;
final double? borderRadius;
final double borderWidth;
@ -33,8 +33,8 @@ class CustomButton extends StatefulWidget {
super.key,
required this.text,
required this.onPressed,
this.backgroundColor = const Color(0xFFED1C2B),
this.borderColor = const Color(0xFFED1C2B),
this.backgroundColor,
this.borderColor,
this.textColor = Colors.white,
this.borderRadius,
this.borderWidth = 2,
@ -93,6 +93,8 @@ class _CustomButtonState extends State<CustomButton> with SingleTickerProviderSt
final iconS = widget.iconSize ?? (24.h);
final fontS = widget.fontSize ?? (16.f);
final isButtonDisabled = widget.isDisabled || widget.onPressed == null;
final resolvedBackgroundColor = widget.backgroundColor ?? AppColors.primaryRedColor;
final resolvedBorderColor = widget.borderColor ?? AppColors.primaryRedColor;
return Semantics(
button: true,
@ -134,10 +136,10 @@ class _CustomButtonState extends State<CustomButton> with SingleTickerProviderSt
width: widget.width,
padding: widget.padding,
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: isButtonDisabled ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : widget.backgroundColor,
color: isButtonDisabled ? AppColors.inputLabelTextColor.withValues(alpha: 0.3) : resolvedBackgroundColor,
borderRadius: radius,
customBorder: BorderRadius.circular(radius),
side: widget.borderSide ?? BorderSide(width: widget.borderWidth.h, color: isButtonDisabled ? Colors.transparent : widget.borderColor)),
side: widget.borderSide ?? BorderSide(width: widget.borderWidth.h, color: isButtonDisabled ? Colors.transparent : resolvedBorderColor)),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,

@ -163,7 +163,7 @@ class _DateRangeSelectorState extends State<DateRangeSelector> {
),
startRangeSelectionColor: AppColors.primaryRedColor,
endRangeSelectionColor: AppColors.primaryRedColor,
rangeSelectionColor: AppColors.primaryRedColor.withOpacity(0.1),
rangeSelectionColor: AppColors.secondaryLightRedColor,
rangeTextStyle: TextStyle(
fontFamily: "Poppins",
),
@ -963,7 +963,7 @@ class _DateRangeSelectorState extends State<DateRangeSelector> {
children: [
AppCustomChipWidget(
labelText: LocaleKeys.thisWeek.tr(context: context),
backgroundColor: model.currentlySelectedRange == Range.WEEKLY ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.whiteColor,
backgroundColor: model.currentlySelectedRange == Range.WEEKLY ? AppColors.secondaryLightRedColor : AppColors.whiteColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: model.currentlySelectedRange == Range.WEEKLY ? AppColors.primaryRedBorderColor : AppColors.chipBorderColorOpacity20,
@ -977,7 +977,7 @@ class _DateRangeSelectorState extends State<DateRangeSelector> {
}),
AppCustomChipWidget(
labelText: LocaleKeys.lastMonth.tr(context: context),
backgroundColor: model.currentlySelectedRange == Range.LAST_MONTH ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.whiteColor,
backgroundColor: model.currentlySelectedRange == Range.LAST_MONTH ? AppColors.secondaryLightRedColor : AppColors.whiteColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: model.currentlySelectedRange == Range.LAST_MONTH ? AppColors.primaryRedBorderColor : AppColors.chipBorderColorOpacity20,
@ -991,7 +991,7 @@ class _DateRangeSelectorState extends State<DateRangeSelector> {
}),
AppCustomChipWidget(
labelText: LocaleKeys.lastSixMonths.tr(context: context),
backgroundColor: model.currentlySelectedRange == Range.LAST_6MONTH ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.whiteColor,
backgroundColor: model.currentlySelectedRange == Range.LAST_6MONTH ? AppColors.secondaryLightRedColor : AppColors.whiteColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: model.currentlySelectedRange == Range.LAST_6MONTH ? AppColors.primaryRedBorderColor : AppColors.chipBorderColorOpacity20,
@ -1012,7 +1012,7 @@ class _DateRangeSelectorState extends State<DateRangeSelector> {
],
),
labelText: "${LocaleKeys.year.tr(context: context)} ${model.getCurrentYear}",
backgroundColor: model.currentlySelectedRange == Range.THIS_YEAR ? AppColors.primaryRedColor.withOpacity(0.1) : AppColors.whiteColor,
backgroundColor: model.currentlySelectedRange == Range.THIS_YEAR ? AppColors.secondaryLightRedColor : AppColors.whiteColor,
shape: RoundedRectangleBorder(
side: BorderSide(
color: model.currentlySelectedRange == Range.THIS_YEAR ? AppColors.primaryRedBorderColor : AppColors.chipBorderColorOpacity20,

@ -56,21 +56,22 @@ class _LanguageSelectorState extends State<LanguageSelector> {
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
// currentLangData['name']?.toUpperCase() ?? 'EN',
widget.currentLanguage == 'ar' ? "ENGLISH" : "العربية",
style: context.dynamicTextStyle(
fontWeight: FontWeight.w600,
fontSize: 14.f,
color: AppColors.primaryRedColor,
letterSpacing: 0.1,
isLanguageSwitcher: true,
fontSize: 14.f,
color: AppColors.primaryRedColor,
letterSpacing: 0.1,
isLanguageSwitcher: true,
fontFamily: widget.currentLanguage == 'ar' ? "Poppins" : "CairoArabic"),
),
SizedBox(width: 6.h),
Utils.buildSvgWithAssets(icon: AppAssets.language),
Utils.buildSvgWithAssets(
icon: AppAssets.language,
iconColor: AppColors.primaryRedColor,
),
],
),
),

@ -220,7 +220,7 @@ class _MapTypeItem extends StatelessWidget {
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 8.h),
decoration: BoxDecoration(
color: isSelected
? AppColors.primaryRedColor.withOpacity(0.1)
? AppColors.secondaryLightRedColor
: Colors.transparent,
borderRadius: BorderRadius.circular(8.r),
),

@ -372,7 +372,7 @@ class _MapTypeItem extends StatelessWidget {
padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 8.h),
decoration: BoxDecoration(
color: isSelected
? AppColors.primaryRedColor.withOpacity(0.1)
? AppColors.secondaryLightRedColor
: Colors.transparent,
borderRadius: BorderRadius.circular(8.r),
),

@ -486,6 +486,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.10.10"
firebase_remote_config:
dependency: "direct main"
description:
name: firebase_remote_config
sha256: e1635b1e8713f4a823920ec3a56a14034b90ce455d47746ab0da994857f370cf
url: "https://pub.dev"
source: hosted
version: "5.5.0"
firebase_remote_config_platform_interface:
dependency: transitive
description:
name: firebase_remote_config_platform_interface
sha256: ce836c5c62056edbe23ef501e6876691ee32476afd12fe95b76e57bba9d25485
url: "https://pub.dev"
source: hosted
version: "2.0.0"
firebase_remote_config_web:
dependency: transitive
description:
name: firebase_remote_config_web
sha256: "9dbd75024bfcd47c05046c95f9cf648a8fc862b096bcf8ea1e4b855d50ea19ad"
url: "https://pub.dev"
source: hosted
version: "1.8.9"
fixnum:
dependency: transitive
description:
@ -797,10 +821,10 @@ packages:
dependency: "direct main"
description:
name: flutter_zoom_videosdk
sha256: "46a4dea664b1c969099328a499c198a1755adf9ac333dea28bea5187910b3bf9"
sha256: "87caad07b994f09b9d8c4b8054c0dff5659029abae38dd6ec223460c3f1bcb7d"
url: "https://pub.dev"
source: hosted
version: "2.1.10"
version: "2.5.10"
fluttertoast:
dependency: "direct main"
description:
@ -1409,6 +1433,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
myfatoorah_flutter:
dependency: "direct main"
description:
name: myfatoorah_flutter
sha256: c9fa65c6329f3c076e48421726b855a444d9790b480d50822452f14f0e56d95f
url: "https://pub.dev"
source: hosted
version: "3.3.2"
ndef:
dependency: transitive
description:

@ -70,6 +70,7 @@ dependencies:
google_api_availability: ^5.0.1
firebase_analytics: ^11.5.1
firebase_crashlytics: ^4.3.8
firebase_remote_config: ^5.5.0
jiffy: ^6.4.3
# hijri_gregorian_calendar:
# path: /Users/aamir/StudioProjects/hijri_gregorian_calendar
@ -113,6 +114,7 @@ dependencies:
cloudflare_turnstile: ^3.7.2
freerasp: ^8.2.1
safe_device: ^1.4.1
myfatoorah_flutter: ^3.3.2
dev_dependencies:
flutter_test:

Loading…
Cancel
Save