diff --git a/assets/json/default_occasion_config.json b/assets/json/default_occasion_config.json
new file mode 100644
index 00000000..6b38a4cb
--- /dev/null
+++ b/assets/json/default_occasion_config.json
@@ -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
+}
+
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 1dc6cf76..391a902b 100644
--- a/ios/Flutter/AppFrameworkInfo.plist
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -20,7 +20,5 @@
????
CFBundleVersion
1.0
- MinimumOSVersion
- 13.0
diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index e3773d42..c3fedb29 100644
--- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -5,6 +5,24 @@
+
+
+
+
+
+
+
+
+
+
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))}';
}
diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart
index ea8f3341..9b73c871 100644
--- a/lib/core/api_consts.dart
+++ b/lib/core/api_consts.dart
@@ -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=';
diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart
index eace4b39..c1d0293d 100644
--- a/lib/core/app_state.dart
+++ b/lib/core/app_state.dart
@@ -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;
diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart
index b6c7fb38..97458e1b 100644
--- a/lib/core/dependencies.dart
+++ b/lib/core/dependencies.dart
@@ -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(
+ appState: getIt(),
+ errorHandlerService: getIt(),
+ payfortRepo: getIt(),
+ ));
+
getIt.registerLazySingleton(
() => ClarityService(appState: getIt.get()),
);
+
+ // Remote-config-driven "occasion" theming (National Day, Eid, etc.).
+ getIt.registerLazySingleton(() => RemoteConfigServiceImp(loggerService: getIt()));
+ getIt.registerLazySingleton(() => OccasionThemeViewModel(
+ remoteConfigService: getIt(),
+ cacheService: getIt(),
+ loggerService: getIt(),
+ ));
}
}
diff --git a/lib/core/myFatoorah_config.dart b/lib/core/myFatoorah_config.dart
new file mode 100644
index 00000000..582ced3c
--- /dev/null
+++ b/lib/core/myFatoorah_config.dart
@@ -0,0 +1,5 @@
+class MyFatoorahConfig {
+
+ static const String configAPIKey = "SK_KWT_0auB76h3Frhj7aUBSZa6H5ei792eZoGAPh0bBxKsrgtyjqKgf38MPnZqzGqagErb";
+
+}
\ No newline at end of file
diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart
index d3107d72..0d09a716 100644
--- a/lib/core/utils/utils.dart
+++ b/lib/core/utils/utils.dart
@@ -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,
diff --git a/lib/features/authentication/authentication_view_model.dart b/lib/features/authentication/authentication_view_model.dart
index 3baecc40..b0764941 100644
--- a/lib/features/authentication/authentication_view_model.dart
+++ b/lib/features/authentication/authentication_view_model.dart
@@ -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));
}
},
);
diff --git a/lib/features/lab/lab_view_model.dart b/lib/features/lab/lab_view_model.dart
index 1c25ef9d..d7f5bb62 100644
--- a/lib/features/lab/lab_view_model.dart
+++ b/lib/features/lab/lab_view_model.dart
@@ -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!);
diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart
index b153c556..b2b990db 100644
--- a/lib/features/my_appointments/my_appointments_view_model.dart
+++ b/lib/features/my_appointments/my_appointments_view_model.dart
@@ -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) {
diff --git a/lib/features/myfatoorah/myfatoorah_view_model.dart b/lib/features/myfatoorah/myfatoorah_view_model.dart
new file mode 100644
index 00000000..b76b251e
--- /dev/null
+++ b/lib/features/myfatoorah/myfatoorah_view_model.dart
@@ -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().getAuthenticatedUser()!.emailAddress;
+ payfortRequestInsertModel.orderDescription = paymentDescription;
+ payfortRequestInsertModel.serviceID = serviceID;
+ payfortRequestInsertModel.paymentOption = paymentOption;
+ payfortRequestInsertModel.clientRequestID = invoiceId;
+ payfortRequestInsertModel.projectID = projectID;
+ payfortRequestInsertModel.fileNumber = getIt.get().getAuthenticatedUser()!.patientId.toString();
+ payfortRequestInsertModel.customerName = "${getIt.get().getAuthenticatedUser()!.firstName} ${getIt.get().getAuthenticatedUser()!.lastName}";
+ payfortRequestInsertModel.currency = getIt.get().getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED";
+ payfortRequestInsertModel.liveServiceID = liveServiceID;
+ payfortRequestInsertModel.latitude = getIt.get().userLat.toString();
+ payfortRequestInsertModel.longitude = getIt.get().userLong.toString();
+ payfortRequestInsertModel.merchantReference = invoiceId;
+ payfortRequestInsertModel.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken);
+ payfortRequestInsertModel.language = getIt.get().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 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 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);
+ }
+ }
+ },
+ );
+ }
+}
diff --git a/lib/features/occasion_theme/occasion_config_model.dart b/lib/features/occasion_theme/occasion_config_model.dart
new file mode 100644
index 00000000..03bf9392
--- /dev/null
+++ b/lib/features/occasion_theme/occasion_config_model.dart
@@ -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 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 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,
+ };
+}
+
diff --git a/lib/features/occasion_theme/occasion_theme_view_model.dart b/lib/features/occasion_theme/occasion_theme_view_model.dart
new file mode 100644
index 00000000..9dd22b09
--- /dev/null
+++ b/lib/features/occasion_theme/occasion_theme_view_model.dart
@@ -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 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 refresh() async {
+ try {
+ await _remoteConfigService.fetchAndActivate();
+ final rawJson = _remoteConfigService.occasionConfigRawJson;
+ _config = OccasionConfigModel.fromJson(json.decode(rawJson) as Map);
+ 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);
+ } 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;
+ }
+}
+
diff --git a/lib/main.dart b/lib/main.dart
index 28f86f0d..5e527909 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -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 callInitializations() async {
// Restore persisted accessibility settings before the first frame.
getIt.get().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();
+ unawaited(occasionThemeViewModel.initialize());
}
void main() async {
@@ -304,9 +315,15 @@ void main() async {
ChangeNotifierProvider(
create: (_) => getIt.get(),
),
+ ChangeNotifierProvider(
+ create: (_) => getIt.get(),
+ ),
ChangeNotifierProvider(
create: (_) => getIt.get(),
- )
+ ),
+ ChangeNotifierProvider(
+ create: (_) => getIt.get(),
+ ),
], 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(
- builder: (context, profileVm, _) {
+ return Consumer2(
+ 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) {
diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart
index dc147981..7d1d3f91 100644
--- a/lib/presentation/appointments/appointment_details_page.dart
+++ b/lib/presentation/appointments/appointment_details_page.dart
@@ -989,7 +989,7 @@ class _AppointmentDetailsPageState extends State {
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 {
openDoctorScheduleCalendar();
},
backgroundColor: AppColors.primaryRedColor,
- borderColor: AppColors.primaryRedColor,
+ borderColor: Colors.transparent,
textColor: Colors.white,
fontSize: 16.f,
fontWeight: FontWeight.w600,
diff --git a/lib/presentation/appointments/my_appointments_page.dart b/lib/presentation/appointments/my_appointments_page.dart
index da20e183..e15edb47 100644
--- a/lib/presentation/appointments/my_appointments_page.dart
+++ b/lib/presentation/appointments/my_appointments_page.dart
@@ -67,8 +67,8 @@ class _MyAppointmentsPageState extends State {
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 {
),
);
},
- 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,
diff --git a/lib/presentation/appointments/widgets/appointment_doctor_card.dart b/lib/presentation/appointments/widgets/appointment_doctor_card.dart
index 4dd838ea..a3b13259 100644
--- a/lib/presentation/appointments/widgets/appointment_doctor_card.dart
+++ b/lib/presentation/appointments/widgets/appointment_doctor_card.dart
@@ -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,
diff --git a/lib/presentation/appointments/widgets/appointment_rating_widget.dart b/lib/presentation/appointments/widgets/appointment_rating_widget.dart
index 9b13c6cd..ae70da1d 100644
--- a/lib/presentation/appointments/widgets/appointment_rating_widget.dart
+++ b/lib/presentation/appointments/widgets/appointment_rating_widget.dart
@@ -142,9 +142,9 @@ class _AppointmentRatingWidgetState extends State {
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);
},
diff --git a/lib/presentation/authentication/quick_login.dart b/lib/presentation/authentication/quick_login.dart
index 133e38ef..00e872a3 100644
--- a/lib/presentation/authentication/quick_login.dart
+++ b/lib/presentation/authentication/quick_login.dart
@@ -94,17 +94,18 @@ class QuickLoginState extends State {
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 {
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",
)),
diff --git a/lib/presentation/authentication/saved_login_screen.dart b/lib/presentation/authentication/saved_login_screen.dart
index bea93d36..4de4bb64 100644
--- a/lib/presentation/authentication/saved_login_screen.dart
+++ b/lib/presentation/authentication/saved_login_screen.dart
@@ -327,9 +327,9 @@ class _SavedLogin extends State 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 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,
diff --git a/lib/presentation/blood_donation/blood_donation_page.dart b/lib/presentation/blood_donation/blood_donation_page.dart
index f3f0782d..b4bdb6df 100644
--- a/lib/presentation/blood_donation/blood_donation_page.dart
+++ b/lib/presentation/blood_donation/blood_donation_page.dart
@@ -232,7 +232,7 @@ class _BloodDonationPageState extends State {
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,
),
diff --git a/lib/presentation/book_appointment/book_appointment_page.dart b/lib/presentation/book_appointment/book_appointment_page.dart
index c2560ecd..57b3d541 100644
--- a/lib/presentation/book_appointment/book_appointment_page.dart
+++ b/lib/presentation/book_appointment/book_appointment_page.dart
@@ -106,8 +106,8 @@ class _BookAppointmentPageState extends State {
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 {
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),
diff --git a/lib/presentation/book_appointment/doctor_profile_page.dart b/lib/presentation/book_appointment/doctor_profile_page.dart
index 5b6ad09d..ee4d859b 100644
--- a/lib/presentation/book_appointment/doctor_profile_page.dart
+++ b/lib/presentation/book_appointment/doctor_profile_page.dart
@@ -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().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;
diff --git a/lib/presentation/book_appointment/laser/laser_appointment.dart b/lib/presentation/book_appointment/laser/laser_appointment.dart
index c8773933..affd5423 100644
--- a/lib/presentation/book_appointment/laser/laser_appointment.dart
+++ b/lib/presentation/book_appointment/laser/laser_appointment.dart
@@ -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()),
diff --git a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart
index 31defa56..2147cbd6 100644
--- a/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart
+++ b/lib/presentation/book_appointment/livecare/immediate_livecare_payment_page.dart
@@ -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= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! &&
@@ -80,11 +92,15 @@ class _ImmediateLiveCarePaymentPageState extends State();
myAppointmentsViewModel = Provider.of(context, listen: false);
immediateLiveCareViewModel = Provider.of(context, listen: false);
payfortViewModel = Provider.of(context, listen: false);
paytabsViewModel = Provider.of(context, listen: false);
+ myFatoorahViewModel = Provider.of(context, listen: false);
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Consumer(builder: (context, myAppointmentsVM, child) {
@@ -131,7 +147,62 @@ class _ImmediateLiveCarePaymentPageState extends State 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 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 SizedBox(
height: 16.h,
),
diff --git a/lib/presentation/home/data/landing_page_data.dart b/lib/presentation/home/data/landing_page_data.dart
index 3d98e5a1..026c1725 100644
--- a/lib/presentation/home/data/landing_page_data.dart
+++ b/lib/presentation/home/data/landing_page_data.dart
@@ -9,192 +9,190 @@ import 'package:hmg_patient_app_new/theme/colors.dart';
class LandingPageData {
static List get getNotLoggedInServiceCardsList => [
- ServiceCardData(
- serviceName: "emergency",
- icon: AppAssets.emergency_services_icon,
- title: getIt.get().isArabic()?LocaleKeys.services3 :LocaleKeys.emergency2,
- subtitle: getIt.get().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().isArabic()? LocaleKeys.calculators:LocaleKeys.health ,
- subtitle: getIt.get().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().isArabic()? LocaleKeys.converters:LocaleKeys.health,
- subtitle: getIt.get().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().isArabic()? LocaleKeys.guide:LocaleKeys.parking2,
- // subtitle: getIt.get().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().isArabic() ? LocaleKeys.services3 : LocaleKeys.emergency2,
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.calculators : LocaleKeys.health,
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.converters : LocaleKeys.health,
+ subtitle: getIt.get().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().isArabic()? LocaleKeys.guide:LocaleKeys.parking2,
+ // subtitle: getIt.get().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 get getLoggedInServiceCardsList => [
- ServiceCardData(
- serviceName: "emergency",
- icon: AppAssets.emergency_services_icon,
- title: getIt.get().isArabic()?LocaleKeys.services3 :LocaleKeys.emergency2,
- subtitle: getIt.get().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().isArabic()?LocaleKeys.labSubtitle:LocaleKeys.lab,
- subtitle: getIt.get().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().isArabic()?LocaleKeys.labSubtitle:LocaleKeys.radiology,
- subtitle: getIt.get().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().isArabic()?LocaleKeys.medicinesSubtitle:LocaleKeys.medicalKey,
- subtitle: getIt.get().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().isArabic()?LocaleKeys.insurance :LocaleKeys.insurance,
- subtitle: getIt.get().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().isArabic() ? LocaleKeys.myDoctorSubtitle2.tr() : "My",
- subtitle: getIt.get().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().isArabic()?LocaleKeys.sickSubtitle:LocaleKeys.sick2,
- subtitle: getIt.get().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().isArabic()?LocaleKeys.myDoctorSubtitle2:LocaleKeys.invoiceList2,
- subtitle: getIt.get().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().isArabic() ? LocaleKeys.services3 : LocaleKeys.emergency2,
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.labSubtitle : LocaleKeys.lab,
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.labSubtitle : LocaleKeys.radiology,
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.medicinesSubtitle : LocaleKeys.medicalKey,
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.insurance : LocaleKeys.insurance,
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.myDoctorSubtitle2.tr() : "My",
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.sickSubtitle : LocaleKeys.sick2,
+ subtitle: getIt.get().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().isArabic() ? LocaleKeys.myDoctorSubtitle2 : LocaleKeys.invoiceList2,
+ subtitle: getIt.get().isArabic() ? LocaleKeys.invoiceList2 : LocaleKeys.myDoctorSubtitle2,
+ backgroundColor: AppColors.whiteColor,
+ iconColor: AppColors.blackColor,
+ textColor: AppColors.blackColor,
+ isBold: false,
+ ),
+ ];
static List 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),
+ ];
}
diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart
index fdb8afcf..8644655d 100644
--- a/lib/presentation/home/landing_page.dart
+++ b/lib/presentation/home/landing_page.dart
@@ -5,6 +5,7 @@ import 'dart:ui' as ui;
import 'package:clarity_flutter/clarity_flutter.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:flutter_swiper_view/flutter_swiper_view.dart';
import 'package:get_it/get_it.dart';
@@ -31,34 +32,30 @@ import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_queue_details_response_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_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/profile_settings/profile_settings_view_model.dart';
-import 'package:hmg_patient_app_new/features/paytabs/paytabs_view_model.dart';
import 'package:hmg_patient_app_new/features/todo_section/todo_section_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/appointments/appointment_queue_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart';
import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
-import 'package:hmg_patient_app_new/presentation/authentication/login.dart';
import 'package:hmg_patient_app_new/presentation/authentication/quick_login.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/book_appointment_page.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/livecare/immediate_livecare_pending_request_page.dart';
import 'package:hmg_patient_app_new/presentation/contact_us/contact_us.dart';
import 'package:hmg_patient_app_new/presentation/hmg_services/services_page.dart';
-import 'package:hmg_patient_app_new/presentation/home/app_update_page.dart';
import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/habib_wallet_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/lakum_wallet_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/large_service_card.dart';
+import 'package:hmg_patient_app_new/presentation/home/widgets/occasion_banner_widget.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/small_service_card.dart';
import 'package:hmg_patient_app_new/presentation/home/widgets/welcome_widget.dart';
import 'package:hmg_patient_app_new/presentation/insurance/widgets/insurance_update_details_card.dart';
import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page.dart';
import 'package:hmg_patient_app_new/presentation/my_family/my_family.dart';
import 'package:hmg_patient_app_new/presentation/notifications/notifications_list_page.dart';
-import 'package:hmg_patient_app_new/presentation/offers_and_discounts/offers_and_discounts_page.dart';
-import 'package:hmg_patient_app_new/presentation/offers_and_discounts/widgets/offers_and_discounts.dart';
import 'package:hmg_patient_app_new/presentation/rate_appointment/rate_appointment_doctor.dart';
-import 'package:hmg_patient_app_new/presentation/tele_consultation/zoom/call_screen.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/ancillary_procedures_details_page.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/todo_page.dart';
import 'package:hmg_patient_app_new/presentation/todo_section/widgets/ancillary_orders_list.dart';
@@ -126,10 +123,7 @@ class _LandingPageState extends State {
authVM.savePushTokenToAppState();
if (mounted) {
final user = appState.getAuthenticatedUser();
- final hasUserQuickLoginData =
- user != null &&
- (user.mobileNumber?.isNotEmpty ?? false) &&
- (user.patientIdentificationNo?.isNotEmpty ?? false);
+ final hasUserQuickLoginData = user != null && (user.mobileNumber?.isNotEmpty ?? false) && (user.patientIdentificationNo?.isNotEmpty ?? false);
if (hasUserQuickLoginData) {
authVM.checkLastLoginStatus(() {
@@ -191,7 +185,7 @@ class _LandingPageState extends State {
onSuccess: ((response) {
// Only open dialog if details are loaded AND widget is mounted
if (!mounted || appointmentRatingViewModel.appointmentDetails == null) return;
-
+
appointmentRatingViewModel.setClinicOrDoctor(false);
appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr());
appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr());
@@ -227,585 +221,695 @@ class _LandingPageState extends State {
child: Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
body: Consumer(builder: (context, insuranceVM, child) {
+ // Only when an occasion theme is active AND it publishes a header
+ // banner do we switch to the sticky-header layout below. Otherwise
+ // (no occasion, or occasion active but no banner configured) the
+ // page behaves exactly as it always has — banner (if any) scrolls
+ // inline with the rest of the content.
+ final hasStickyBanner = context.watch().headerImage?.isNotEmpty ?? false;
+
return Stack(
children: [
- RefreshIndicator(
- color: AppColors.primaryRedColor,
- onRefresh: () async {
- if (appState.isAuthenticated) {
- // Refresh Appointments Data
- myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
- myAppointmentsViewModel.initAppointmentsViewModel();
- myAppointmentsViewModel.getPatientAppointments(true, false, isForTimeLine: true);
-
- // Refresh Appointments Data
- // habibWalletVM.initHabibWalletProvider();
- // habibWalletVM.getPatientBalanceAmount();
- // habibWalletVM.getLakumAccountInformation();
-
- // Refresh Ancillary Orders
- todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true);
- todoSectionViewModel.initializeTodoSectionViewModel();
-
- // Refresh Immediate LiveCare Data
- immediateLiveCareViewModel.initImmediateLiveCare();
- immediateLiveCareViewModel.getPatientLiveCareHistory();
-
- // appointmentRatingViewModel.getLastRatingAppointment(
- // onSuccess: (response) {
- // if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
- // appointmentRatingViewModel.getAppointmentDetails(
- // appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!,
- // appointmentRatingViewModel.appointmentRatedList.last.projectID!,
- // onSuccess: ((response) {
- // appointmentRatingViewModel.setClinicOrDoctor(false);
- // appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context));
- // appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context));
- // openLastRating();
- // appState.setRatedVisible(true);
- // }),
- // );
- // }
- // },
- // );
-
- }
- },
- child: SingleChildScrollView(
- controller: _scrollController,
- physics: const AlwaysScrollableScrollPhysics(),
- padding: EdgeInsets.only(
- top: (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown)
- ? (MediaQuery.paddingOf(context).top + 70.h)
- : kToolbarHeight + 16.h,
- bottom: 24.h),
- child: Column(
- spacing: 16.h,
- children: [
- Row(
- spacing: 8.h,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- appState.isAuthenticated
- ? WelcomeWidget(
- onTap: () {
- // ...existing navigation code...
-
- Navigator.of(context).push(
- CustomPageRoute(
- direction: AxisDirection.down,
- page: FamilyMedicalScreen(),
- ),
- );
-
- // Navigator.pushReplacementNamed(
- // // context,
- // GetIt.instance().navigatorKey.currentContext!,
- // AppRoutes.zoomCallPage,
- // // arguments: CallArguments(appointmentID, "111", "Patient", "40", "1", true, 1),
- // arguments: CallArguments("yosemite-338", "123", "Patient", "40", "0", true, 1),
- // // arguments: CallArguments("SmallDailyStandup9875", "123", "Patient", "40", "0", false, int.parse(widget.incomingCallData!.appointmentNo!)),
- // );
- },
- name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'),
- imageWidget: Selector(
- selector: (_, profileVM) => profileVM.profileImageData ?? appState.getProfileImageData,
- shouldRebuild: (previous, next) => previous != next,
- builder: (context, profileImageData, child) {
- // Only rebuild when profile image data changes
- return UserAvatarWidget(
- key: ValueKey('landing_avatar_${profileImageData?.hashCode ?? 0}'),
- width: 42.w,
- height: 42.h,
- fit: BoxFit.cover,
- isCircular: true,
- customProfileImageData: profileImageData,
- );
- },
- ),
- ).expanded
- : CustomButton(
- text: LocaleKeys.loginOrRegister.tr(context: context),
- onPressed: () async {
- await authVM.onLoginPressed();
-
- // Navigator.pushReplacementNamed(
- // // context,
- // GetIt.instance().navigatorKey.currentContext!,
- // AppRoutes.zoomCallPage,
- // arguments: CallArguments("lake-tahoe-289", "123", "Patient", "40", "0", true, 1),
- // );
-
- // Navigator.of(context).push(
- // CustomPageRoute(
- // // page: NotificationsListPage(),
- // page: AppUpdatePage(
- // appUpdateMessage:
- // "A new update for Dr.Alhabib app is now available with new features and services.\r\nFor technical support:\r\neservices.hmg@drsulaimanalhabib.com"),
- // ),
- // );
-
- // ...existing commented code...
- },
- backgroundColor: AppColors.secondaryLightRedColor,
- borderColor: AppColors.secondaryLightRedColor,
- textColor: AppColors.primaryRedColor,
- fontSize: 14.f,
- fontWeight: FontWeight.w700,
- borderRadius: 12.r,
- padding: EdgeInsets.fromLTRB(12.h, 0, 12.h, 0),
- height: 40.h,
- ),
- Consumer(builder: (context, todoSectionVM, child) {
- return Row(
- mainAxisSize: MainAxisSize.min,
- // spacing: 18.h,
+ // Renders nothing unless a remote-config occasion theme (e.g.
+ // National Day) is currently active — fills the whole page,
+ // behind all other content below.
+ const OccasionBackgroundWidget(),
+
+ if (hasStickyBanner)
+ // Android 15+ (targetSdk 36) enforces edge-to-edge and
+ // silently ignores SystemUiOverlayStyle.statusBarColor, so
+ // the status bar's background must be painted manually.
+ // AppColors.primaryRedColor already resolves to the active
+ // occasion's color, so this needs no extra plumbing.
+ Positioned(
+ top: 0,
+ left: 0,
+ right: 0,
+ child: Container(
+ height: MediaQuery.paddingOf(context).top,
+ color: AppColors.primaryRedColor,
+ ),
+ ),
+
+ Column(
+ children: [
+ // Pinned/sticky header — only rendered here when an
+ // occasion banner is active; scrollable content below
+ // fills the remaining space and scrolls independently.
+ // Only the banner itself is offset clear of the status
+ // bar — the scrollable content underneath intentionally
+ // keeps extending edge-to-edge behind it (unchanged from
+ // before), with the AnimatedOpacity gradient smoothing
+ // that transition as the user scrolls.
+ if (hasStickyBanner)
+ Padding(
+ padding: EdgeInsets.only(top: MediaQuery.paddingOf(context).top - 12.h),
+ child: const OccasionBannerWidget(),
+ ),
+ Expanded(
+ child: RefreshIndicator(
+ color: AppColors.primaryRedColor,
+ onRefresh: () async {
+ // Re-check Remote Config for occasion theme changes on
+ // pull-to-refresh (independent of auth state) — handy for
+ // testing without needing to restart the app.
+ unawaited(GetIt.instance().refresh());
+
+ if (appState.isAuthenticated) {
+ // Refresh Appointments Data
+ myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
+ myAppointmentsViewModel.initAppointmentsViewModel();
+ myAppointmentsViewModel.getPatientAppointments(true, false, isForTimeLine: true);
+
+ // Refresh Appointments Data
+ // habibWalletVM.initHabibWalletProvider();
+ // habibWalletVM.getPatientBalanceAmount();
+ // habibWalletVM.getLakumAccountInformation();
+
+ // Refresh Ancillary Orders
+ todoSectionViewModel.setIsAncillaryOrdersNeedReloading(true);
+ todoSectionViewModel.initializeTodoSectionViewModel();
+
+ // Refresh Immediate LiveCare Data
+ immediateLiveCareViewModel.initImmediateLiveCare();
+ immediateLiveCareViewModel.getPatientLiveCareHistory();
+
+ // appointmentRatingViewModel.getLastRatingAppointment(
+ // onSuccess: (response) {
+ // if (appointmentRatingViewModel.appointmentRatedList.isNotEmpty) {
+ // appointmentRatingViewModel.getAppointmentDetails(
+ // appointmentRatingViewModel.appointmentRatedList.last.appointmentNo!,
+ // appointmentRatingViewModel.appointmentRatedList.last.projectID!,
+ // onSuccess: ((response) {
+ // appointmentRatingViewModel.setClinicOrDoctor(false);
+ // appointmentRatingViewModel.setTitle(LocaleKeys.rateDoctor.tr(context: context));
+ // appointmentRatingViewModel.setSubTitle(LocaleKeys.howWasYourLastVisitWithDoctor.tr(context: context));
+ // openLastRating();
+ // appState.setRatedVisible(true);
+ // }),
+ // );
+ // }
+ // },
+ // );
+ }
+ },
+ child: SingleChildScrollView(
+ controller: _scrollController,
+ physics: const AlwaysScrollableScrollPhysics(),
+ padding: EdgeInsets.only(
+ top: (appState.isAuthenticated && !insuranceVM.isInsuranceLoading && insuranceVM.isInsuranceExpired && insuranceVM.isInsuranceExpiryBannerShown)
+ ? (MediaQuery.paddingOf(context).top + 70.h)
+ // When the occasion banner is pinned above (hasStickyBanner), it
+ // already occupies the toolbar-height space that this padding
+ // otherwise reserves — so only a small breathing gap is needed.
+ : (hasStickyBanner ? 16.h : kToolbarHeight + 16.h),
+ bottom: 24.h),
+ child: Column(
+ children: [
+ if (!hasStickyBanner) const OccasionBannerWidget(),
+ Row(
+ spacing: 8.h,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Stack(clipBehavior: Clip.none, children: [
- if (appState.isAuthenticated) Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 24.h, width: 24.h),
- (appState.isAuthenticated && todoSectionVM.notificationsCount != null && todoSectionVM.notificationsCount != "0")
- ? Positioned(
- // right: appState.isArabic() ? 8.w : -8.w,
- top: -8.h,
- // left: 4.h,
- // bottom: 4.h,
- child: Container(
- width: 18.w,
- height: 18.h,
- padding: EdgeInsets.all(2),
- decoration: BoxDecoration(
- color: AppColors.primaryRedColor,
- borderRadius: BorderRadius.circular(20.r),
+ appState.isAuthenticated
+ ? WelcomeWidget(
+ onTap: () {
+ // ...existing navigation code...
+
+ Navigator.of(context).push(
+ CustomPageRoute(
+ direction: AxisDirection.down,
+ page: FamilyMedicalScreen(),
),
- child: Text(
- todoSectionVM.notificationsCount ?? "0",
- style: TextStyle(
- color: Colors.white,
- fontFamily: "Poppins",
- fontSize: 10.f,
- fontWeight: FontWeight.w600,
- ),
- textAlign: TextAlign.center,
+ );
+
+ // Navigator.pushReplacementNamed(
+ // // context,
+ // GetIt.instance().navigatorKey.currentContext!,
+ // AppRoutes.zoomCallPage,
+ // // arguments: CallArguments(appointmentID, "111", "Patient", "40", "1", true, 1),
+ // arguments: CallArguments("yosemite-338", "123", "Patient", "40", "0", true, 1),
+ // // arguments: CallArguments("SmallDailyStandup9875", "123", "Patient", "40", "0", false, int.parse(widget.incomingCallData!.appointmentNo!)),
+ // );
+ },
+ name: ('${appState.getAuthenticatedUser()!.firstName!} ${appState.getAuthenticatedUser()!.lastName!}'),
+ imageWidget: Selector(
+ selector: (_, profileVM) => profileVM.profileImageData ?? appState.getProfileImageData,
+ shouldRebuild: (previous, next) => previous != next,
+ builder: (context, profileImageData, child) {
+ // Only rebuild when profile image data changes
+ return UserAvatarWidget(
+ key: ValueKey('landing_avatar_${profileImageData?.hashCode ?? 0}'),
+ width: 42.w,
+ height: 42.h,
+ fit: BoxFit.cover,
+ isCircular: true,
+ customProfileImageData: profileImageData,
+ );
+ },
+ ),
+ ).expanded
+ : CustomButton(
+ text: LocaleKeys.loginOrRegister.tr(context: context),
+ onPressed: () async {
+ await authVM.onLoginPressed();
+
+ // Navigator.pushReplacementNamed(
+ // // context,
+ // GetIt.instance().navigatorKey.currentContext!,
+ // AppRoutes.zoomCallPage,
+ // arguments: CallArguments("lake-tahoe-289", "123", "Patient", "40", "0", true, 1),
+ // );
+
+ // Navigator.of(context).push(
+ // CustomPageRoute(
+ // // page: NotificationsListPage(),
+ // page: AppUpdatePage(
+ // appUpdateMessage:
+ // "A new update for Dr.Alhabib app is now available with new features and services.\r\nFor technical support:\r\neservices.hmg@drsulaimanalhabib.com"),
+ // ),
+ // );
+
+ // ...existing commented code...
+ },
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: Colors.transparent,
+ textColor: AppColors.primaryRedColor,
+ fontSize: 14.f,
+ fontWeight: FontWeight.w700,
+ borderRadius: 10.r,
+ padding: EdgeInsets.fromLTRB(12.h, 0, 12.h, 0),
+ height: 40.h,
+ ),
+ Consumer(builder: (context, todoSectionVM, child) {
+ return Row(
+ mainAxisSize: MainAxisSize.min,
+ // spacing: 18.h,
+ children: [
+ Stack(clipBehavior: Clip.none, children: [
+ if (appState.isAuthenticated) Utils.buildSvgWithAssets(icon: AppAssets.bell, height: 24.h, width: 24.h),
+ (appState.isAuthenticated && todoSectionVM.notificationsCount != null && todoSectionVM.notificationsCount != "0")
+ ? Positioned(
+ // right: appState.isArabic() ? 8.w : -8.w,
+ top: -8.h,
+ // left: 4.h,
+ // bottom: 4.h,
+ child: Container(
+ width: 18.w,
+ height: 18.h,
+ padding: EdgeInsets.all(2),
+ decoration: BoxDecoration(
+ color: AppColors.primaryRedColorBase,
+ borderRadius: BorderRadius.circular(20.r),
+ ),
+ child: Text(
+ todoSectionVM.notificationsCount ?? "0",
+ style: TextStyle(
+ color: Colors.white,
+ fontFamily: "Poppins",
+ fontSize: 10.f,
+ fontWeight: FontWeight.w600,
+ ),
+ textAlign: TextAlign.center,
+ ),
+ ),
+ )
+ : SizedBox.shrink(),
+ ]).onPress(() async {
+ if (appState.isAuthenticated) {
+ notificationsViewModel.setNotificationStatusID(2);
+ notificationsViewModel.getAllNotifications();
+ Navigator.of(context).push(
+ CustomPageRoute(
+ page: NotificationsListPage(),
+ // page: LoginScreen(),
),
- ),
- )
- : SizedBox.shrink(),
- ]).onPress(() async {
- if (appState.isAuthenticated) {
- notificationsViewModel.setNotificationStatusID(2);
- notificationsViewModel.getAllNotifications();
- Navigator.of(context).push(
- CustomPageRoute(
- page: NotificationsListPage(),
- // page: LoginScreen(),
+ );
+ } else {
+ await authVM.onLoginPressed();
+ }
+ }),
+ SizedBox(
+ width: 24.w,
),
- );
- } else {
- await authVM.onLoginPressed();
- }
- }),
- SizedBox(
- width: 24.w,
- ),
- Utils.buildSvgWithAssets(icon: AppAssets.location, height: 24.h, width: 24.w).onPress(() {
- // openIndoorNavigationBottomSheet(context);
- showCommonBottomSheetWithoutHeight(
- context,
- title: LocaleKeys.contactUs.tr(),
- child: ContactUs(),
- callBackFunc: () {},
- isFullScreen: false,
+ Utils.buildSvgWithAssets(icon: AppAssets.location, height: 24.h, width: 24.w).onPress(() {
+ // openIndoorNavigationBottomSheet(context);
+ showCommonBottomSheetWithoutHeight(
+ context,
+ title: LocaleKeys.contactUs.tr(),
+ child: ContactUs(),
+ callBackFunc: () {},
+ isFullScreen: false,
+ );
+ }),
+
+ // Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 24.h, width: 24.h).onPress(() {
+ // showCommonBottomSheetWithoutHeight(
+ // context,
+ // title: LocaleKeys.contactUs.tr(),
+ // child: ContactUs(),
+ // callBackFunc: () {},
+ // isFullScreen: false,
+ // );
+ // }),
+ !appState.isAuthenticated
+ ? Row(children: [
+ SizedBox(width: 24.w),
+ Utils.buildSvgWithAssets(icon: appState.isArabic() ? AppAssets.enLangIcon : AppAssets.arLangIcon, height: 24.h, width: 24.h).onPress(() {
+ context.setLocale(appState.isArabic() ? Locale('en', 'US') : Locale('ar', 'SA'));
+ })
+ ])
+ : SizedBox()
+ ],
);
}),
-
- // Utils.buildSvgWithAssets(icon: AppAssets.contact_icon, height: 24.h, width: 24.h).onPress(() {
- // showCommonBottomSheetWithoutHeight(
- // context,
- // title: LocaleKeys.contactUs.tr(),
- // child: ContactUs(),
- // callBackFunc: () {},
- // isFullScreen: false,
- // );
- // }),
- !appState.isAuthenticated
- ? Row(children: [
- SizedBox(width: 24.w),
- Utils.buildSvgWithAssets(icon: appState.isArabic() ? AppAssets.enLangIcon : AppAssets.arLangIcon, height: 24.h, width: 24.h).onPress(() {
- context.setLocale(appState.isArabic() ? Locale('en', 'US') : Locale('ar', 'SA'));
- })
- ])
- : SizedBox()
],
- );
- }),
- ],
- ).paddingSymmetrical(24.h, 0.h),
- !appState.isAuthenticated
- ? Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24.r,
- hasShadow: false,
- ),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Row(
- children: [
- Utils.buildSvgWithAssets(
- width: 50.w,
- height: 60.h,
- icon: AppAssets.symptomCheckerIcon,
- fit: BoxFit.contain,
+ ).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 8.h),
+ !appState.isAuthenticated
+ ? Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: false,
),
- SizedBox(width: 12.w),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- LocaleKeys.howAreYouFeelingToday.tr(context: context).toText14(isBold: true),
- LocaleKeys.checkYourSymptomsWithScale.tr(context: context).toText12(isBold: true),
- SizedBox(height: 14.h),
- CustomButton(
- text: LocaleKeys.checkYourSymptoms.tr(context: context),
- onPressed: () async {
- context.navigateWithName(AppRoutes.userInfoSelection);
- },
- padding: EdgeInsetsGeometry.zero,
- backgroundColor: AppColors.primaryRedColor,
- borderColor: AppColors.primaryRedColor,
- textColor: Colors.white,
- fontSize: 14.f,
- fontWeight: FontWeight.w600,
- borderRadius: 12.r,
- height: 40.h,
- ),
- ],
- ).expanded
- ],
- ),
- ),
- ).paddingSymmetrical(24.w, 0.h)
- : SizedBox.shrink(),
-
- // Offers And Discounts Carousel - Auto-scrolling from right to left
- // appState.isAuthenticated && appState.isEnabledOffersAndDiscountsCarousel
- // ? Column(
- // children: [
- // SizedBox(height: 12.h),
- // Row(
- // mainAxisAlignment: MainAxisAlignment.spaceBetween,
- // children: [
- // ("${LocaleKeys.offers.tr(context: context)} & ${LocaleKeys.discount.tr(context: context)}").toText16(isBold: true),
- // Row(
- // children: [
- // LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),
- // SizedBox(width: 2.h),
- // Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
- // ],
- // ),
- // ],
- // ).paddingSymmetrical(24.h, 0.h).onPress(() {
- // Navigator.of(context).push(CustomPageRoute(page: OffersAndDiscountsPage()));
- // }),
- // SizedBox(height: 16.h),
- // OffersAndDiscountsCarousel().paddingSymmetrical(24.h, 0.h),
- // SizedBox(height: 18.h),
- // ],
- // )
- // : SizedBox.shrink(),
-
- appState.isAuthenticated
- ? Column(
- children: [
- SizedBox(height: 12.h),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- LocaleKeys.appointmentsAndVisits.tr(context: context).toText16(isBold: true),
- Row(
- children: [
- LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),
- SizedBox(width: 2.h),
- Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
- ],
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Row(
+ children: [
+ Utils.buildSvgWithAssets(
+ width: 50.w,
+ height: 60.h,
+ icon: AppAssets.symptomCheckerIcon,
+ fit: BoxFit.contain,
+ ),
+ SizedBox(width: 12.w),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.howAreYouFeelingToday.tr(context: context).toText14(isBold: true),
+ LocaleKeys.checkYourSymptomsWithScale.tr(context: context).toText12(isBold: true),
+ SizedBox(height: 14.h),
+ CustomButton(
+ text: LocaleKeys.checkYourSymptoms.tr(context: context),
+ onPressed: () async {
+ context.navigateWithName(AppRoutes.userInfoSelection);
+ },
+ padding: EdgeInsetsGeometry.zero,
+ backgroundColor: AppColors.primaryRedColor,
+ borderColor: AppColors.primaryRedColor,
+ textColor: Colors.white,
+ fontSize: 14.f,
+ fontWeight: FontWeight.w600,
+ borderRadius: 12.r,
+ height: 40.h,
+ ),
+ ],
+ ).expanded
+ ],
+ ),
),
- ],
- ).paddingSymmetrical(24.h, 0.h).onPress(() {
- myAppointmentsViewModel.onTabChange(0);
- myAppointmentsViewModel.updateListWRTTab(0);
-
- Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage()));
- // getIt.get().setPaymentConfiguration("Appointment Payment", 15.5);
- // getIt.get().startCardPayment();
- }),
- Consumer3(
- builder: (context, myAppointmentsVM, immediateLiveCareVM, todoSectionVM, child) {
- return myAppointmentsVM.isMyAppointmentsLoading
- ? Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24.r,
- hasShadow: true,
- ),
- child: AppointmentCard(
- patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
- myAppointmentsViewModel: myAppointmentsViewModel,
- bookAppointmentsViewModel: bookAppointmentsViewModel,
- isLoading: true,
- isFromHomePage: true,
- ),
- ).paddingSymmetrical(24.h, 16.h)
- : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
- ? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 && !immediateLiveCareVM.patientHasPendingLiveCareRequest
- ? Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- color: AppColors.whiteColor,
- borderRadius: 24.r,
- hasShadow: true,
- ),
- child: AppointmentCard(
- patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first,
- myAppointmentsViewModel: myAppointmentsViewModel,
- bookAppointmentsViewModel: bookAppointmentsViewModel,
- isLoading: false,
- isFromHomePage: true,
- ),
- ).paddingSymmetrical(24.h, 0.h)
- : isTablet
- ? SizedBox(
- height: isTablet ? 290.h : 255.h,
- child: ListView.separated(
- scrollDirection: Axis.horizontal,
- itemCount: () {
- final int baseCount =
- myAppointmentsVM.patientAppointmentsHistoryList.length + (immediateLiveCareVM.patientHasPendingLiveCareRequest ? 1 : 0);
- return baseCount < 3 ? baseCount : 3;
- }(),
- shrinkWrap: true,
- padding: EdgeInsets.only(left: 16.h, right: 16.h),
- itemBuilder: (context, index) {
- return SizedBox(
- height: isTablet ? 290.h : 255.h,
- width: 250.w,
- child: getIndexSwiperCard(index),
- );
- // return AnimationConfiguration.staggeredList(
- // position: index,
- // duration: const Duration(milliseconds: 1000),
- // child: SlideAnimation(
- // horizontalOffset: 100.0,
- // child: FadeInAnimation(
- // child: SizedBox(
- // height: 255.h,
- // width: 250.w,
- // child: getIndexSwiperCard(index),
- // ),
- // ),
- // ),
- // );
- },
- separatorBuilder: (BuildContext cxt, int index) => SizedBox(
- width: 10.w,
+ ).paddingSymmetrical(24.w, 0.h)
+ : SizedBox.shrink(),
+ SizedBox(height: 16.h),
+
+ // Offers And Discounts Carousel - Auto-scrolling from right to left
+ // appState.isAuthenticated && appState.isEnabledOffersAndDiscountsCarousel
+ // ? Column(
+ // children: [
+ // SizedBox(height: 12.h),
+ // Row(
+ // mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ // children: [
+ // ("${LocaleKeys.offers.tr(context: context)} & ${LocaleKeys.discount.tr(context: context)}").toText16(isBold: true),
+ // Row(
+ // children: [
+ // LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),
+ // SizedBox(width: 2.h),
+ // Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
+ // ],
+ // ),
+ // ],
+ // ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ // Navigator.of(context).push(CustomPageRoute(page: OffersAndDiscountsPage()));
+ // }),
+ // SizedBox(height: 16.h),
+ // OffersAndDiscountsCarousel().paddingSymmetrical(24.h, 0.h),
+ // SizedBox(height: 18.h),
+ // ],
+ // )
+ // : SizedBox.shrink(),
+
+ appState.isAuthenticated
+ ? Column(
+ children: [
+ SizedBox(height: 12.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ LocaleKeys.appointmentsAndVisits.tr(context: context).toText16(isBold: true),
+ Row(
+ children: [
+ LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),
+ SizedBox(width: 2.h),
+ Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
+ ],
+ ),
+ ],
+ ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ myAppointmentsViewModel.onTabChange(0);
+ myAppointmentsViewModel.updateListWRTTab(0);
+
+ Navigator.of(context).push(CustomPageRoute(page: MyAppointmentsPage()));
+ // getIt.get().setPaymentConfiguration("Appointment Payment", 15.5);
+ // getIt.get().startCardPayment();
+ }),
+ Consumer3(
+ builder: (context, myAppointmentsVM, immediateLiveCareVM, todoSectionVM, child) {
+ return myAppointmentsVM.isMyAppointmentsLoading
+ ? Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
+ ),
+ child: AppointmentCard(
+ patientAppointmentHistoryResponseModel: PatientAppointmentHistoryResponseModel(),
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: true,
+ isFromHomePage: true,
+ ),
+ ).paddingSymmetrical(24.h, 16.h)
+ : myAppointmentsVM.patientAppointmentsHistoryList.isNotEmpty
+ ? myAppointmentsVM.patientAppointmentsHistoryList.length == 1 && !immediateLiveCareVM.patientHasPendingLiveCareRequest
+ ? Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ color: AppColors.whiteColor,
+ borderRadius: 24.r,
+ hasShadow: true,
),
- ),
- )
- : SizedBox(
- height: 255.h + 20 + 30, // itemHeight + shadow padding (10 top + 10 bottom) + pagination dots space
- child: Builder(
- builder: (context) {
- final int baseAppointmentCount = myAppointmentsVM.patientAppointmentsHistoryList.length;
- final int liveCareCount = immediateLiveCareVM.patientHasPendingLiveCareRequest ? 1 : 0;
- final int totalCount = baseAppointmentCount + liveCareCount;
-
- final int swiperItemCount = myAppointmentsVM.isMyAppointmentsLoading
- ? 3
- : totalCount < 3
- ? totalCount
- : 3;
- final bool isArabic = appState.isArabic();
- return Swiper(
- itemCount: swiperItemCount,
- layout: SwiperLayout.STACK,
- loop: false,
- itemWidth: MediaQuery.of(context).size.width - 48.h,
- indicatorLayout: PageIndicatorLayout.COLOR,
- axisDirection: isArabic ? AxisDirection.right : AxisDirection.left,
- controller: _controller,
- index: isArabic ? swiperItemCount - 1 : 0,
- itemHeight: 255.h + 20,
- // extra space for shadow
- pagination: SwiperPagination(
- alignment: Alignment.bottomCenter,
- margin: EdgeInsets.only(top: 220.h + 20 + 8 + 24),
- builder: _DirectionalDotPaginationBuilder(
- color: Color(0xffD9D9D9),
- activeColor: AppColors.blackBgColor,
- isRTL: isArabic,
- totalCount: swiperItemCount,
+ child: AppointmentCard(
+ patientAppointmentHistoryResponseModel: myAppointmentsVM.patientAppointmentsHistoryList.first,
+ myAppointmentsViewModel: myAppointmentsViewModel,
+ bookAppointmentsViewModel: bookAppointmentsViewModel,
+ isLoading: false,
+ isFromHomePage: true,
+ ),
+ ).paddingSymmetrical(24.h, 0.h)
+ : isTablet
+ ? SizedBox(
+ height: isTablet ? 290.h : 255.h,
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: () {
+ final int baseCount =
+ myAppointmentsVM.patientAppointmentsHistoryList.length + (immediateLiveCareVM.patientHasPendingLiveCareRequest ? 1 : 0);
+ return baseCount < 3 ? baseCount : 3;
+ }(),
+ shrinkWrap: true,
+ padding: EdgeInsets.only(left: 16.h, right: 16.h),
+ itemBuilder: (context, index) {
+ return SizedBox(
+ height: isTablet ? 290.h : 255.h,
+ width: 250.w,
+ child: getIndexSwiperCard(index),
+ );
+ // return AnimationConfiguration.staggeredList(
+ // position: index,
+ // duration: const Duration(milliseconds: 1000),
+ // child: SlideAnimation(
+ // horizontalOffset: 100.0,
+ // child: FadeInAnimation(
+ // child: SizedBox(
+ // height: 255.h,
+ // width: 250.w,
+ // child: getIndexSwiperCard(index),
+ // ),
+ // ),
+ // ),
+ // );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(
+ width: 10.w,
),
),
- itemBuilder: (BuildContext context, int index) {
- final int actualIndex = isArabic ? (swiperItemCount - 1 - index) : index;
- return Padding(
- padding: const EdgeInsets.symmetric(vertical: 10),
- child: getIndexSwiperCard(actualIndex),
- );
- },
- );
- },
+ )
+ : SizedBox(
+ height: 255.h + 20 + 30, // itemHeight + shadow padding (10 top + 10 bottom) + pagination dots space
+ child: Builder(
+ builder: (context) {
+ final int baseAppointmentCount = myAppointmentsVM.patientAppointmentsHistoryList.length;
+ final int liveCareCount = immediateLiveCareVM.patientHasPendingLiveCareRequest ? 1 : 0;
+ final int totalCount = baseAppointmentCount + liveCareCount;
+
+ final int swiperItemCount = myAppointmentsVM.isMyAppointmentsLoading
+ ? 3
+ : totalCount < 3
+ ? totalCount
+ : 3;
+ final bool isArabic = appState.isArabic();
+ return Swiper(
+ itemCount: swiperItemCount,
+ layout: SwiperLayout.STACK,
+ loop: false,
+ itemWidth: MediaQuery.of(context).size.width - 48.h,
+ indicatorLayout: PageIndicatorLayout.COLOR,
+ axisDirection: isArabic ? AxisDirection.right : AxisDirection.left,
+ controller: _controller,
+ index: isArabic ? swiperItemCount - 1 : 0,
+ itemHeight: 255.h + 20,
+ // extra space for shadow
+ pagination: SwiperPagination(
+ alignment: Alignment.bottomCenter,
+ margin: EdgeInsets.only(top: 220.h + 20 + 8 + 24),
+ builder: _DirectionalDotPaginationBuilder(
+ color: Color(0xffD9D9D9),
+ activeColor: AppColors.blackBgColor,
+ isRTL: isArabic,
+ totalCount: swiperItemCount,
+ ),
+ ),
+ itemBuilder: (BuildContext context, int index) {
+ final int actualIndex = isArabic ? (swiperItemCount - 1 - index) : index;
+ return Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10),
+ child: getIndexSwiperCard(actualIndex),
+ );
+ },
+ );
+ },
+ ),
+ )
+ : immediateLiveCareVM.patientHasPendingLiveCareRequest
+ ? _buildLiveCareRequestCard().paddingSymmetrical(24.h, 16.h)
+ : Container(
+ width: double.infinity,
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
+ child: Padding(
+ padding: EdgeInsets.all(16.h),
+ child: Column(
+ children: [
+ Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
+ SizedBox(height: 12.h),
+ LocaleKeys.noUpcomingAppointmentPleaseBook.tr(context: context).toText12(isCenter: true),
+ SizedBox(height: 12.h),
+ CustomButton(
+ text: LocaleKeys.bookAppo.tr(context: context),
+ onPressed: () {
+ getIt.get().onTabChanged(0);
+ Navigator.of(context).push(CustomPageRoute(page: BookAppointmentPage()));
+ },
+ backgroundColor: AppColors.secondaryLightRedColor,
+ borderColor: AppColors.secondaryLightRedColor,
+ textColor: AppColors.primaryRedColor,
+ fontSize: 14.f,
+ fontWeight: FontWeight.w600,
+ padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
+ icon: AppAssets.add_icon,
+ iconColor: AppColors.primaryRedColor,
+ height: 40.h,
+ ),
+ ],
+ ),
+ ),
+ ).paddingSymmetrical(24.h, 16.h);
+ },
+ ),
+
+ // Consumer for ER Online Check-In pending request
+ // Consumer(
+ // builder: (context, emergencyServicesVM, child) {
+ // return emergencyServicesVM.patientHasAdvanceERBalance
+ // ? Column(
+ // children: [
+ // SizedBox(height: 16.h),
+ // Container(
+ // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
+ // color: AppColors.whiteColor,
+ // borderRadius: 20.r,
+ // hasShadow: false,
+ // side: BorderSide(color: AppColors.primaryRedColor, width: 3.h),
+ // ),
+ // width: double.infinity,
+ // child: Padding(
+ // padding: EdgeInsets.all(16.h),
+ // child: Column(
+ // crossAxisAlignment: CrossAxisAlignment.start,
+ // children: [
+ // // Row(
+ // // mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ // // children: [
+ // // AppCustomChipWidget(
+ // // labelText: LocaleKeys.erOnlineCheckInRequest.tr(context: context),
+ // // backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.10),
+ // // textColor: AppColors.primaryRedColor,
+ // // ),
+ // // Utils.buildSvgWithAssets(icon: AppAssets.appointment_checkin_icon, width: 24.h, height: 24.h, iconColor: AppColors.primaryRedColor),
+ // // ],
+ // // ),
+ // SizedBox(height: 8.h),
+ // Row(
+ // mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ // children: [
+ // LocaleKeys.youHaveEROnlineCheckInRequest.tr(context: context).toText12(isBold: true),
+ // Transform.flip(
+ // flipX: getIt.get().isArabic(),
+ // child: Utils.buildSvgWithAssets(
+ // icon: AppAssets.forward_arrow_icon_small,
+ // iconColor: AppColors.blackColor,
+ // width: 20.h,
+ // height: 15.h,
+ // fit: BoxFit.contain,
+ // ),
+ // ),
+ // ],
+ // ),
+ // ],
+ // ),
+ // ),
+ // ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ // Navigator.of(context).push(CustomPageRoute(page: ErOnlineCheckinHome()));
+ // // context.read().navigateToEROnlineCheckIn();
+ // }),
+ // SizedBox(height: 12.h),
+ // ],
+ // )
+ // : SizedBox(height: 0.h);
+ // },
+ // ),
+ SizedBox(height: 16.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ LocaleKeys.quickLinks.tr(context: context).toText16(isBold: true),
+ Row(
+ children: [
+ LocaleKeys.viewMedicalFileLandingPage.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),
+ SizedBox(width: 2.h),
+ Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
+ ],
+ ),
+ ],
+ ).paddingSymmetrical(24.h, 0.h).onPress(() {
+ Navigator.of(context).push(CustomPageRoute(page: MedicalFilePage()));
+ }),
+ SizedBox(height: 16.h),
+ Consumer(builder: (BuildContext context, TodoSectionViewModel todoSectionVM, Widget? child) {
+ return Container(
+ decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
+ child: Column(
+ children: [
+ todoSectionVM.patientAncillaryOrdersList.isNotEmpty
+ ? Container(
+ height: 50.h,
+ decoration: ShapeDecoration(
+ color: AppColors.eReferralCardColor.withAlpha(50),
+ shape: SmoothRectangleBorder(
+ borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
+ smoothness: 1,
),
- )
- : immediateLiveCareVM.patientHasPendingLiveCareRequest
- ? _buildLiveCareRequestCard().paddingSymmetrical(24.h, 16.h)
- : Container(
- width: double.infinity,
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r, hasShadow: true),
- child: Padding(
- padding: EdgeInsets.all(16.h),
- child: Column(
+ ),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- Utils.buildSvgWithAssets(icon: AppAssets.home_calendar_icon, width: 32.h, height: 32.h),
- SizedBox(height: 12.h),
- LocaleKeys.noUpcomingAppointmentPleaseBook.tr(context: context).toText12(isCenter: true),
- SizedBox(height: 12.h),
+ LocaleKeys.pendingAncillaryOrders
+ .tr(context: context)
+ .toText14(color: AppColors.eReferralCardColor, isBold: true)
+ .paddingSymmetrical(24.h, 0.h),
CustomButton(
- text: LocaleKeys.bookAppo.tr(context: context),
+ text: LocaleKeys.view.tr(context: context),
onPressed: () {
- getIt.get().onTabChanged(0);
- Navigator.of(context).push(CustomPageRoute(page: BookAppointmentPage()));
+ todoSectionVM.setIsAncillaryOrdersNeedReloading(true);
+ Navigator.of(context).push(CustomPageRoute(page: ToDoPage()));
},
- backgroundColor: Color(0xffFEE9EA),
- borderColor: Color(0xffFEE9EA),
- textColor: Color(0xffED1C2B),
- fontSize: 14.f,
+ backgroundColor: AppColors.eReferralCardColor,
+ borderColor: AppColors.eReferralCardColor,
+ textColor: AppColors.whiteColor,
+ fontSize: 10.f,
fontWeight: FontWeight.w600,
- padding: EdgeInsets.fromLTRB(10.h, 0, 10.h, 0),
- icon: AppAssets.add_icon,
- iconColor: AppColors.primaryRedColor,
- height: 40.h,
- ),
+ borderRadius: 8,
+ padding: EdgeInsets.fromLTRB(15, 0, 15, 0),
+ height: 30.h,
+ ).paddingSymmetrical(24.h, 0.h),
],
),
- ),
- ).paddingSymmetrical(24.h, 16.h);
- },
- ),
-
- // Consumer for ER Online Check-In pending request
- // Consumer(
- // builder: (context, emergencyServicesVM, child) {
- // return emergencyServicesVM.patientHasAdvanceERBalance
- // ? Column(
- // children: [
- // SizedBox(height: 16.h),
- // Container(
- // decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
- // color: AppColors.whiteColor,
- // borderRadius: 20.r,
- // hasShadow: false,
- // side: BorderSide(color: AppColors.primaryRedColor, width: 3.h),
- // ),
- // width: double.infinity,
- // child: Padding(
- // padding: EdgeInsets.all(16.h),
- // child: Column(
- // crossAxisAlignment: CrossAxisAlignment.start,
- // children: [
- // // Row(
- // // mainAxisAlignment: MainAxisAlignment.spaceBetween,
- // // children: [
- // // AppCustomChipWidget(
- // // labelText: LocaleKeys.erOnlineCheckInRequest.tr(context: context),
- // // backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.10),
- // // textColor: AppColors.primaryRedColor,
- // // ),
- // // Utils.buildSvgWithAssets(icon: AppAssets.appointment_checkin_icon, width: 24.h, height: 24.h, iconColor: AppColors.primaryRedColor),
- // // ],
- // // ),
- // SizedBox(height: 8.h),
- // Row(
- // mainAxisAlignment: MainAxisAlignment.spaceBetween,
- // children: [
- // LocaleKeys.youHaveEROnlineCheckInRequest.tr(context: context).toText12(isBold: true),
- // Transform.flip(
- // flipX: getIt.get().isArabic(),
- // child: Utils.buildSvgWithAssets(
- // icon: AppAssets.forward_arrow_icon_small,
- // iconColor: AppColors.blackColor,
- // width: 20.h,
- // height: 15.h,
- // fit: BoxFit.contain,
- // ),
- // ),
- // ],
- // ),
- // ],
- // ),
- // ),
- // ).paddingSymmetrical(24.h, 0.h).onPress(() {
- // Navigator.of(context).push(CustomPageRoute(page: ErOnlineCheckinHome()));
- // // context.read().navigateToEROnlineCheckIn();
- // }),
- // SizedBox(height: 12.h),
- // ],
- // )
- // : SizedBox(height: 0.h);
- // },
- // ),
- SizedBox(height: 16.h),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- LocaleKeys.quickLinks.tr(context: context).toText16(isBold: true),
- Row(
- children: [
- LocaleKeys.viewMedicalFileLandingPage.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),
- SizedBox(width: 2.h),
- Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
- ],
- ),
- ],
- ).paddingSymmetrical(24.h, 0.h).onPress(() {
- Navigator.of(context).push(CustomPageRoute(page: MedicalFilePage()));
- }),
- SizedBox(height: 16.h),
- Consumer(builder: (BuildContext context, TodoSectionViewModel todoSectionVM, Widget? child) {
- return Container(
+ )
+ : SizedBox.shrink(),
+ SizedBox(
+ height: 92.h + 32.h - 4.h,
+ child: RawScrollbar(
+ controller: _horizontalScrollController,
+ thumbVisibility: true,
+ radius: Radius.circular(10.0),
+ thumbColor: AppColors.primaryRedColor,
+ trackVisibility: true,
+ trackColor: Color(0xffD9D9D9),
+ trackBorderColor: Colors.transparent,
+ trackRadius: Radius.circular(10.0),
+ padding: EdgeInsets.only(top: 92.h + 32.h, left: MediaQuery.sizeOf(context).width / 2.5 - 10, right: MediaQuery.sizeOf(context).width / 2.5 - 10),
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: LandingPageData().getLoggedInServiceCardsList.length,
+ shrinkWrap: true,
+ controller: _horizontalScrollController,
+ padding: EdgeInsets.only(left: 0.h, right: 0.h, top: 16.h, bottom: 12.h),
+ itemBuilder: (context, index) {
+ return AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 200),
+ child: SlideAnimation(
+ horizontalOffset: 100.0,
+ child: FadeInAnimation(
+ child: SmallServiceCard(
+ icon: LandingPageData().getLoggedInServiceCardsList[index].icon,
+ title: LandingPageData().getLoggedInServiceCardsList[index].title,
+ subtitle: LandingPageData().getLoggedInServiceCardsList[index].subtitle,
+ iconColor: LandingPageData().getLoggedInServiceCardsList[index].iconColor!,
+ textColor: LandingPageData().getLoggedInServiceCardsList[index].textColor,
+ backgroundColor: LandingPageData().getLoggedInServiceCardsList[index].backgroundColor,
+ isBold: LandingPageData().getLoggedInServiceCardsList[index].isBold,
+ serviceName: LandingPageData().getLoggedInServiceCardsList[index].serviceName,
+ ),
+ ),
+ ),
+ );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => 10.width,
+ ).paddingSymmetrical(16.h, 0.h),
+ ),
+ ),
+ SizedBox(height: 16.h),
+ ],
+ ),
+ ).paddingSymmetrical(24.h, 0.h);
+ }),
+ ],
+ )
+ : Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
child: Column(
children: [
- todoSectionVM.patientAncillaryOrdersList.isNotEmpty
- ? Container(
- height: 50.h,
- decoration: ShapeDecoration(
- color: AppColors.eReferralCardColor.withAlpha(50),
- shape: SmoothRectangleBorder(
- borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)),
- smoothness: 1,
- ),
- ),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- LocaleKeys.pendingAncillaryOrders.tr(context: context).toText14(color: AppColors.eReferralCardColor, isBold: true).paddingSymmetrical(24.h, 0.h),
- CustomButton(
- text: LocaleKeys.view.tr(context: context),
- onPressed: () {
- todoSectionVM.setIsAncillaryOrdersNeedReloading(true);
- Navigator.of(context).push(CustomPageRoute(page: ToDoPage()));
- },
- backgroundColor: AppColors.eReferralCardColor,
- borderColor: AppColors.eReferralCardColor,
- textColor: AppColors.whiteColor,
- fontSize: 10.f,
- fontWeight: FontWeight.w600,
- borderRadius: 8,
- padding: EdgeInsets.fromLTRB(15, 0, 15, 0),
- height: 30.h,
- ).paddingSymmetrical(24.h, 0.h),
- ],
- ),
- )
- : SizedBox.shrink(),
SizedBox(
height: 92.h + 32.h - 4.h,
child: RawScrollbar(
@@ -820,160 +924,112 @@ class _LandingPageState extends State {
padding: EdgeInsets.only(top: 92.h + 32.h, left: MediaQuery.sizeOf(context).width / 2.5 - 10, right: MediaQuery.sizeOf(context).width / 2.5 - 10),
child: ListView.separated(
scrollDirection: Axis.horizontal,
- itemCount: LandingPageData().getLoggedInServiceCardsList.length,
+ itemCount: LandingPageData.getNotLoggedInServiceCardsList.length,
shrinkWrap: true,
controller: _horizontalScrollController,
padding: EdgeInsets.only(left: 0.h, right: 0.h, top: 16.h, bottom: 12.h),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
- duration: const Duration(milliseconds: 200),
+ duration: const Duration(milliseconds: 1000),
child: SlideAnimation(
horizontalOffset: 100.0,
child: FadeInAnimation(
child: SmallServiceCard(
- icon: LandingPageData().getLoggedInServiceCardsList[index].icon,
- title: LandingPageData().getLoggedInServiceCardsList[index].title,
- subtitle: LandingPageData().getLoggedInServiceCardsList[index].subtitle,
- iconColor: LandingPageData().getLoggedInServiceCardsList[index].iconColor!,
- textColor: LandingPageData().getLoggedInServiceCardsList[index].textColor,
- backgroundColor: LandingPageData().getLoggedInServiceCardsList[index].backgroundColor,
- isBold: LandingPageData().getLoggedInServiceCardsList[index].isBold,
- serviceName: LandingPageData().getLoggedInServiceCardsList[index].serviceName,
+ serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName,
+ icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon,
+ title: LandingPageData.getNotLoggedInServiceCardsList[index].title,
+ subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle,
+ iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor!,
+ textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor,
+ backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor,
+ isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold,
),
),
),
);
},
- separatorBuilder: (BuildContext cxt, int index) => 10.width,
+ separatorBuilder: (BuildContext cxt, int index) => 0.width,
).paddingSymmetrical(16.h, 0.h),
),
),
SizedBox(height: 16.h),
],
),
- ).paddingSymmetrical(24.h, 0.h);
+ ).paddingSymmetrical(24.h, 0.h),
+ SizedBox(height: 16.h),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ LocaleKeys.services2.tr(context: context).toText18(isBold: true),
+ Row(
+ children: [
+ LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),
+ SizedBox(width: 2.h),
+ Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
+ ],
+ ).onPress(() {
+ Navigator.of(context).push(CustomPageRoute(page: ServicesPage()));
}),
],
- )
- : Container(
- decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.r),
- child: Column(
- children: [
- SizedBox(
- height: 92.h + 32.h - 4.h,
- child: RawScrollbar(
- controller: _horizontalScrollController,
- thumbVisibility: true,
- radius: Radius.circular(10.0),
- thumbColor: AppColors.primaryRedColor,
- trackVisibility: true,
- trackColor: Color(0xffD9D9D9),
- trackBorderColor: Colors.transparent,
- trackRadius: Radius.circular(10.0),
- padding: EdgeInsets.only(top: 92.h + 32.h, left: MediaQuery.sizeOf(context).width / 2.5 - 10, right: MediaQuery.sizeOf(context).width / 2.5 - 10),
- child: ListView.separated(
- scrollDirection: Axis.horizontal,
- itemCount: LandingPageData.getNotLoggedInServiceCardsList.length,
- shrinkWrap: true,
- controller: _horizontalScrollController,
- padding: EdgeInsets.only(left: 0.h, right: 0.h, top: 16.h, bottom: 12.h),
- itemBuilder: (context, index) {
- return AnimationConfiguration.staggeredList(
- position: index,
- duration: const Duration(milliseconds: 1000),
- child: SlideAnimation(
- horizontalOffset: 100.0,
- child: FadeInAnimation(
- child: SmallServiceCard(
- serviceName: LandingPageData.getNotLoggedInServiceCardsList[index].serviceName,
- icon: LandingPageData.getNotLoggedInServiceCardsList[index].icon,
- title: LandingPageData.getNotLoggedInServiceCardsList[index].title,
- subtitle: LandingPageData.getNotLoggedInServiceCardsList[index].subtitle,
- iconColor: LandingPageData.getNotLoggedInServiceCardsList[index].iconColor!,
- textColor: LandingPageData.getNotLoggedInServiceCardsList[index].textColor,
- backgroundColor: LandingPageData.getNotLoggedInServiceCardsList[index].backgroundColor,
- isBold: LandingPageData.getNotLoggedInServiceCardsList[index].isBold,
- ),
- ),
- ),
- );
- },
- separatorBuilder: (BuildContext cxt, int index) => 0.width,
- ).paddingSymmetrical(16.h, 0.h),
+ ).paddingSymmetrical(24.w, 0.h),
+ SizedBox(height: 16.h),
+ ConstrainedBox(
+ constraints: BoxConstraints(maxHeight: isFoldable ? 450.h : (isTablet ? 440.h : 431.h), minHeight: 411.h),
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: LandingPageData.getServiceCardsList.length,
+ shrinkWrap: true,
+ padding: EdgeInsets.only(left: 24.w, right: 24.w),
+ itemBuilder: (context, index) {
+ return AnimationConfiguration.staggeredList(
+ position: index,
+ duration: const Duration(milliseconds: 1000),
+ child: SlideAnimation(
+ horizontalOffset: 100.0,
+ child: FadeInAnimation(
+ child: FadedLargeServiceCard(
+ serviceCardData: LandingPageData.getServiceCardsList[index],
+ image: LandingPageData.getServiceCardsList[index].icon,
+ title: LandingPageData.getServiceCardsList[index].title,
+ subtitle: LandingPageData.getServiceCardsList[index].subtitle,
+ icon: LandingPageData.getServiceCardsList[index].largeCardIcon,
+ isPNG: LandingPageData.getServiceCardsList[index].isPNG,
+ ),
+ ),
),
- ),
- SizedBox(height: 16.h),
- ],
- ),
- ).paddingSymmetrical(24.h, 0.h),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- LocaleKeys.services2.tr(context: context).toText18(isBold: true),
- Row(
- children: [
- LocaleKeys.viewAll.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true),
- SizedBox(width: 2.h),
- Icon(Icons.arrow_forward_ios, color: AppColors.primaryRedColor, size: 14.h),
- ],
- ).onPress(() {
- Navigator.of(context).push(CustomPageRoute(page: ServicesPage()));
- }),
- ],
- ).paddingSymmetrical(24.w, 0.h),
- ConstrainedBox(
- constraints: BoxConstraints(maxHeight: isFoldable ? 450.h : (isTablet ? 440.h : 431.h), minHeight: 411.h),
- child: ListView.separated(
- scrollDirection: Axis.horizontal,
- itemCount: LandingPageData.getServiceCardsList.length,
- shrinkWrap: true,
- padding: EdgeInsets.only(left: 24.w, right: 24.w),
- itemBuilder: (context, index) {
- return AnimationConfiguration.staggeredList(
- position: index,
- duration: const Duration(milliseconds: 1000),
- child: SlideAnimation(
- horizontalOffset: 100.0,
- child: FadeInAnimation(
- child: FadedLargeServiceCard(
- serviceCardData: LandingPageData.getServiceCardsList[index],
- image: LandingPageData.getServiceCardsList[index].icon,
- title: LandingPageData.getServiceCardsList[index].title,
- subtitle: LandingPageData.getServiceCardsList[index].subtitle,
- icon: LandingPageData.getServiceCardsList[index].largeCardIcon,
- isPNG: LandingPageData.getServiceCardsList[index].isPNG,
- ),
- ),
+ );
+ },
+ separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.w),
),
- );
- },
- separatorBuilder: (BuildContext cxt, int index) => SizedBox(width: 16.w),
+ ),
+ SizedBox(height: 16.h),
+ appState.isAuthenticated
+ ? Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ LocaleKeys.myBalanceSubtitle.tr(context: context).toText16(isBold: true).paddingSymmetrical(24.h, 0.h),
+ SizedBox(
+ height: 180.h,
+ child: ListView.separated(
+ scrollDirection: Axis.horizontal,
+ itemCount: 2,
+ padding: EdgeInsetsDirectional.only(start: 24.h, end: 24.h),
+ itemBuilder: (BuildContext context, int index) {
+ return index == 0 ? HabibWalletCard() : LakumWalletCard();
+ },
+ separatorBuilder: (BuildContext context, int index) => SizedBox(width: 12.h),
+ ),
+ ),
+ ],
+ )
+ : SizedBox(),
+ ],
),
),
- appState.isAuthenticated
- ? Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- LocaleKeys.myBalanceSubtitle.tr(context: context).toText16(isBold: true).paddingSymmetrical(24.h, 0.h),
- SizedBox(
- height: 180.h,
- child: ListView.separated(
- scrollDirection: Axis.horizontal,
- itemCount: 2,
- padding: EdgeInsetsDirectional.only(start: 24.h, end: 24.h),
- itemBuilder: (BuildContext context, int index) {
- return index == 0 ? HabibWalletCard() : LakumWalletCard();
- },
- separatorBuilder: (BuildContext context, int index) => SizedBox(width: 12.h),
- ),
- ),
- ],
- )
- : SizedBox(),
- ],
+ ),
),
- ),
+ ],
),
AnimatedOpacity(
opacity: _scrollOpacity,
@@ -998,9 +1054,9 @@ class _LandingPageState extends State {
? Container(
height: MediaQuery.paddingOf(context).top + 50.h,
decoration: ShapeDecoration(
- color: AppColors.secondaryLightRedBorderColor,
+ color: AppColors.secondaryLightRedColorBase,
shape: SmoothRectangleBorder(
- side: BorderSide(width: 1, color: AppColors.primaryRedColor.withAlpha(20)),
+ side: BorderSide(width: 1, color: AppColors.primaryRedColorBase.withAlpha(20)),
// borderRadius: BorderRadius.only(bottomLeft: Radius.circular(24), bottomRight: Radius.circular(24)),
smoothness: 1,
),
@@ -1011,7 +1067,7 @@ class _LandingPageState extends State {
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
- LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColor, isBold: true).paddingSymmetrical(0.h, 0.h),
+ LocaleKeys.insuranceExpiredOrInactive.tr(context: context).toText14(color: AppColors.primaryRedColorBase, isBold: true).paddingSymmetrical(0.h, 0.h),
Row(
children: [
CustomButton(
@@ -1023,8 +1079,8 @@ class _LandingPageState extends State {
showCommonBottomSheetWithoutHeight(context,
child: PatientInsuranceCardUpdateCard(), callBackFunc: () {}, title: "", isCloseButtonVisible: false, isFullScreen: false);
},
- backgroundColor: AppColors.primaryRedColor,
- borderColor: AppColors.secondaryLightRedBorderColor,
+ backgroundColor: AppColors.primaryRedColorBase,
+ borderColor: AppColors.primaryRedColorBase,
textColor: Colors.white,
fontSize: 12.f,
fontWeight: FontWeight.bold,
@@ -1032,7 +1088,7 @@ class _LandingPageState extends State {
padding: EdgeInsets.fromLTRB(15, 0, 15, 0),
height: 36.h,
).paddingSymmetrical(12.h, 0.h),
- Icon(Icons.close, color: AppColors.primaryRedColor).onPress(() {
+ Icon(Icons.close, color: AppColors.primaryRedColorBase).onPress(() {
insuranceVM.setIsInsuranceExpiryBannerShown(false);
}),
],
diff --git a/lib/presentation/home/widgets/habib_wallet_card.dart b/lib/presentation/home/widgets/habib_wallet_card.dart
index 803b9b5a..604ed5f5 100644
--- a/lib/presentation/home/widgets/habib_wallet_card.dart
+++ b/lib/presentation/home/widgets/habib_wallet_card.dart
@@ -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),
diff --git a/lib/presentation/home/widgets/lakum_wallet_card.dart b/lib/presentation/home/widgets/lakum_wallet_card.dart
index feea02ba..cddfd309 100644
--- a/lib/presentation/home/widgets/lakum_wallet_card.dart
+++ b/lib/presentation/home/widgets/lakum_wallet_card.dart
@@ -92,7 +92,7 @@ class LakumWalletCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.end,
children: [
(habibWalletVM.isLakumPointsHidden
- ? "••••"
+ ? "****"
: NumberFormat.decimalPattern()
.format(habibWalletVM.isLakumAccountInfoLoading
? 123
diff --git a/lib/presentation/home/widgets/occasion_banner_widget.dart b/lib/presentation/home/widgets/occasion_banner_widget.dart
new file mode 100644
index 00000000..1f19b826
--- /dev/null
+++ b/lib/presentation/home/widgets/occasion_banner_widget.dart
@@ -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(
+ 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(
+ 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);
+}
diff --git a/lib/presentation/home/widgets/small_service_card.dart b/lib/presentation/home/widgets/small_service_card.dart
index 45023c3b..3c5ebbf7 100644
--- a/lib/presentation/home/widgets/small_service_card.dart
+++ b/lib/presentation/home/widgets/small_service_card.dart
@@ -38,7 +38,7 @@ class SmallServiceCard extends StatelessWidget {
final Color? backgroundColor;
final bool isBold;
- SmallServiceCard({
+ const SmallServiceCard({
super.key,
this.icon = "",
this.serviceName = "",
diff --git a/lib/presentation/insurance/widgets/insurance_history.dart b/lib/presentation/insurance/widgets/insurance_history.dart
index 0f5bde1e..f2019c91 100644
--- a/lib/presentation/insurance/widgets/insurance_history.dart
+++ b/lib/presentation/insurance/widgets/insurance_history.dart
@@ -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,
diff --git a/lib/presentation/insurance/widgets/patient_insurance_card.dart b/lib/presentation/insurance/widgets/patient_insurance_card.dart
index 6fab990c..87ae3a97 100644
--- a/lib/presentation/insurance/widgets/patient_insurance_card.dart
+++ b/lib/presentation/insurance/widgets/patient_insurance_card.dart
@@ -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),
// ),
],
diff --git a/lib/presentation/lab/alphabeticScroll.dart b/lib/presentation/lab/alphabeticScroll.dart
index ff0efbf4..a1a9add8 100644
--- a/lib/presentation/lab/alphabeticScroll.dart
+++ b/lib/presentation/lab/alphabeticScroll.dart
@@ -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 alpahbetsAvailable;
+class AlphabeticScroll extends StatefulWidget {
+ final List alpahbetsAvailable;
final List details;
final AppState appState;
final LabViewModel labViewModel;
@@ -38,13 +38,13 @@ class _AlphabetScrollPageState extends State {
Map density = {};
Map _offsetMap = {};
- Map _offsetKeys = {};
+ Map _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 {
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 {
// 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 {
}
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 {
@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(
- 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(
+ 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 {
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(),
- 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(),
+ 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) =>
//
//
//
// )
- ),
+ ),
);
},
);
diff --git a/lib/presentation/lab/lab_order_by_test.dart b/lib/presentation/lab/lab_order_by_test.dart
index 7740a596..733fb792 100644
--- a/lib/presentation/lab/lab_order_by_test.dart
+++ b/lib/presentation/lab/lab_order_by_test.dart
@@ -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(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(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,
),
- ],
- ),
- ],
- ),
- ));
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ );
}
}
diff --git a/lib/presentation/lab/lab_orders_page.dart b/lib/presentation/lab/lab_orders_page.dart
index 0e8890ba..332a89da 100644
--- a/lib/presentation/lab/lab_orders_page.dart
+++ b/lib/presentation/lab/lab_orders_page.dart
@@ -1 +1 @@
-import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'alphabeticScroll.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@override
State createState() => _LabOrdersPageState();
}
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
late DateRangeSelectorRangeViewModel rangeViewModel;
late AppState _appState;
List?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
int activeIndex = 0;
@override
void initState() {
scheduleMicrotask(() {
labProvider.initLabProvider();
});
super.initState();
}
@override
Widget build(BuildContext context) {
labProvider = Provider.of(context, listen: false);
rangeViewModel = Provider.of(context);
_appState = getIt();
return CollapsingListView(
title: LocaleKeys.labResults.tr(context: context),
physics: activeIndex == 1 ? NeverScrollableScrollPhysics() : ScrollPhysics(),
search: () async {
if (labProvider.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
labProvider.filterLabReports(value);
}
}
},
child: Consumer(
builder: (context, labViewModel, child) {
return SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: CustomTabBar(
activeTextColor: Color(0xffED1C2B),
activeBackgroundColor: Color(0xffED1C2B).withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, LocaleKeys.byVisit.tr()),
CustomTabBarModel(null, LocaleKeys.byTest.tr()),
],
onTabChange: (index) {
activeIndex = index;
setState(() {});
},
),
),
],
),
if (activeIndex == 0)
Padding(
padding: EdgeInsets.symmetric(vertical: 10.h),
child: Row(
children: [
// CustomButton(
// text: LocaleKeys.byClinic.tr(context: context),
// onPressed: () {
// labViewModel.setIsSortByClinic(true);
// },
// backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
// borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
// textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
// fontSize: 12.f,
// isBold: true,
// borderRadius: 10,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// ),
// SizedBox(width: 8.h),
// CustomButton(
// text: LocaleKeys.byHospital.tr(context: context),
// onPressed: () {
// labViewModel.setIsSortByClinic(false);
// },
// backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
// borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
// textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
// fontSize: 12,
// isBold: true,
// borderRadius: 10,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// ),
],
),
),
SizedBox(height: 8.h),
selectedFilterText!.isNotEmpty
? Column(
children: [
AppCustomChipWidget(
labelText: selectedFilterText!,
backgroundColor: AppColors.alertColor,
textColor: AppColors.whiteColor,
deleteIcon: AppAssets.close_bottom_sheet_icon,
deleteIconColor: AppColors.whiteColor,
deleteIconHasColor: true,
isEnglishOnly: true,
onDeleteTap: () {
selectedFilterText = "";
labProvider.filterLabReports("");
},
),
SizedBox(height: 8.h),
],
)
: SizedBox(),
activeIndex == 0
? // By Visit - show grouped view when available
labViewModel.isLabOrdersLoading
? ListView.builder(
shrinkWrap: true,
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: 5,
itemBuilder: (context, index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
),
)
: (labViewModel.patientLabOrders.isNotEmpty
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: labViewModel.filteredLabOrders.length,
itemBuilder: (context, index) {
final group = labViewModel.filteredLabOrders[index];
final isExpanded = expandedIndex == index;
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
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: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
richText: Row(
children: [
"${group.testDetails!.length} ".toText10(
color: AppColors.successColor,
isEnglishOnly: true,
weight: FontWeight.w600),
LocaleKeys.tests
.tr(context: context)
.toText10(color: AppColors.successColor, weight: FontWeight.w600),
],
),
labelText:
"${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}",
backgroundColor: AppColors.successColor.withOpacity(0.1),
textColor: AppColors.successColor,
),
AppCustomChipWidget(
labelText:
"${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}",
backgroundColor: AppColors.warningColorYellow.withOpacity(0.1),
textColor: AppColors.warningColorYellow,
)
],
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
group.doctorImageURL ??
"https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 24.w,
height: 24.h,
fit: BoxFit.cover,
).circle(100),
SizedBox(width: 8.h),
Expanded(
child: (group.doctorName ?? group.doctorNameEnglish ?? "")
.toString()
.toText14(isBold: true)),
],
),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
Directionality(
textDirection: ui.TextDirection.ltr,
child: AppCustomChipWidget(
labelText: DateUtil.formatDateToDate(
DateUtil.convertStringToDate(group.orderDate ?? ""), false),
isEnglishOnly: true,
)),
AppCustomChipWidget(
labelText: (group.projectName ?? ""),
),
AppCustomChipWidget(
labelText: (group.clinicDescription ?? ""),
),
],
),
],
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 500),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey(index),
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h),
child: Column(
children: [
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (cxt, index) {
PatientLabOrdersResponseModel order = group;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"• ${order.testDetails![index].description!}"
.toText14(isBold: true, isEnglishOnly: true),
SizedBox(height: 4.h),
(_appState.isArabic() ? order.testDetails![index].testDescriptionAr! : order.testDetails![index].testDescriptionEn!).toText12(
isBold: true, color: AppColors.textColorLight, isEnglishOnly: true),
],
).paddingOnly(top: 16, bottom: 16);
},
separatorBuilder: (cxt, index) => Divider(
color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length),
SizedBox(height: 16.h),
CustomButton(
text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})",
onPressed: () {
labProvider.currentlySelectedPatientOrder = group;
labProvider.getPatientLabResultByHospital(group);
labProvider.getPatientSpecialResult(group);
Navigator.of(context).push(
CustomPageRoute(
page: LabResultByClinic(labOrder: group),
),
);
},
backgroundColor: AppColors.infoColor.withAlpha(20),
borderColor: AppColors.infoColor.withAlpha(0),
textColor: AppColors.infoColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
isBold: true,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h,
iconSize: 14.h,
icon: AppAssets.view_report_icon,
iconColor: AppColors.infoColor,
),
SizedBox(height: 16.h),
],
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
));
},
)
: Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context)))
: // By Test or other tabs keep existing behavior
(labViewModel.isLabOrdersLoading)
? Column(
children: List.generate(
5,
(index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)),
)
: AlphabeticScroll(
alpahbetsAvailable: labViewModel.filteredIndexedCharacterForUniqueTest,
details: labViewModel.filteredUniqueTestsList,
labViewModel: labViewModel,
rangeViewModel: rangeViewModel,
appState: _appState,
)
],
));
},
),
);
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
String getLabOrderStatusText(num status) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
getLabSuggestions(LabViewModel model) {
if (model.patientLabOrders.isEmpty) {
return [];
}
return model.patientLabOrders.map((m) => m.testDetails).toList();
}
}
\ No newline at end of file
+import 'dart:async';
import 'dart:ui' as ui;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/dependencies.dart';
import 'package:hmg_patient_app_new/core/utils/date_util.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart';
import 'package:hmg_patient_app_new/features/lab/models/resp_models/patient_lab_orders_response_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_item_view.dart';
import 'package:hmg_patient_app_new/presentation/lab/lab_result_via_clinic/LabResultByClinic.dart';
import 'package:hmg_patient_app_new/presentation/lab/search_lab_report.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/chip/app_custom_chip_widget.dart';
import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
import 'alphabeticScroll.dart';
class LabOrdersPage extends StatefulWidget {
const LabOrdersPage({super.key});
@override
State createState() => _LabOrdersPageState();
}
class _LabOrdersPageState extends State {
late LabViewModel labProvider;
late DateRangeSelectorRangeViewModel rangeViewModel;
late AppState _appState;
List?> labSuggestions = [];
int? expandedIndex;
String? selectedFilterText = '';
int activeIndex = 0;
@override
void initState() {
scheduleMicrotask(() {
labProvider.initLabProvider();
});
super.initState();
}
@override
Widget build(BuildContext context) {
labProvider = Provider.of(context, listen: false);
rangeViewModel = Provider.of(context);
_appState = getIt();
return CollapsingListView(
title: LocaleKeys.labResults.tr(context: context),
physics: activeIndex == 1 ? NeverScrollableScrollPhysics() : ScrollPhysics(),
search: () async {
if (labProvider.isLabOrdersLoading) {
return;
} else {
String? value = await Navigator.of(context).push(
CustomPageRoute(
page: SearchLabResultsContent(labSuggestionsList: labProvider.labSuggestions),
fullScreenDialog: true,
direction: AxisDirection.down,
),
);
if (value != null) {
selectedFilterText = value;
labProvider.filterLabReports(value);
}
}
},
child: Consumer(
builder: (context, labViewModel, child) {
return SingleChildScrollView(
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.all(24.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(
child: CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.secondaryLightRedColor,
tabs: [
CustomTabBarModel(null, LocaleKeys.byVisit.tr()),
CustomTabBarModel(null, LocaleKeys.byTest.tr()),
],
onTabChange: (index) {
activeIndex = index;
setState(() {});
},
),
),
],
),
if (activeIndex == 0)
Padding(
padding: EdgeInsets.symmetric(vertical: 10.h),
child: Row(
children: [
// CustomButton(
// text: LocaleKeys.byClinic.tr(context: context),
// onPressed: () {
// labViewModel.setIsSortByClinic(true);
// },
// backgroundColor: labViewModel.isSortByClinic ? AppColors.bgRedLightColor : AppColors.whiteColor,
// borderColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.textColor.withValues(alpha: 0.2),
// textColor: labViewModel.isSortByClinic ? AppColors.primaryRedColor : AppColors.blackColor,
// fontSize: 12.f,
// isBold: true,
// borderRadius: 10,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// ),
// SizedBox(width: 8.h),
// CustomButton(
// text: LocaleKeys.byHospital.tr(context: context),
// onPressed: () {
// labViewModel.setIsSortByClinic(false);
// },
// backgroundColor: labViewModel.isSortByClinic ? AppColors.whiteColor : AppColors.bgRedLightColor,
// borderColor: labViewModel.isSortByClinic ? AppColors.textColor.withValues(alpha: 0.2) : AppColors.primaryRedColor,
// textColor: labViewModel.isSortByClinic ? AppColors.blackColor : AppColors.primaryRedColor,
// fontSize: 12,
// isBold: true,
// borderRadius: 10,
// padding: EdgeInsets.fromLTRB(10, 0, 10, 0),
// height: 40.h,
// ),
],
),
),
SizedBox(height: 8.h),
selectedFilterText!.isNotEmpty
? Column(
children: [
AppCustomChipWidget(
labelText: selectedFilterText!,
backgroundColor: AppColors.alertColor,
textColor: AppColors.whiteColor,
deleteIcon: AppAssets.close_bottom_sheet_icon,
deleteIconColor: AppColors.whiteColor,
deleteIconHasColor: true,
isEnglishOnly: true,
onDeleteTap: () {
selectedFilterText = "";
labProvider.filterLabReports("");
},
),
SizedBox(height: 8.h),
],
)
: SizedBox(),
activeIndex == 0
? // By Visit - show grouped view when available
labViewModel.isLabOrdersLoading
? ListView.builder(
shrinkWrap: true,
physics: AlwaysScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: 5,
itemBuilder: (context, index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
),
)
: (labViewModel.patientLabOrders.isNotEmpty
? ListView.builder(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemCount: labViewModel.filteredLabOrders.length,
itemBuilder: (context, index) {
final group = labViewModel.filteredLabOrders[index];
final isExpanded = expandedIndex == index;
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(
child: AnimatedContainer(
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: InkWell(
onTap: () {
setState(() {
expandedIndex = isExpanded ? null : index;
});
},
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
AppCustomChipWidget(
richText: Row(
children: [
"${group.testDetails!.length} ".toText10(color: AppColors.successColor, isEnglishOnly: true, weight: FontWeight.w600),
LocaleKeys.tests.tr(context: context).toText10(color: AppColors.successColor, weight: FontWeight.w600),
],
),
labelText: "${group.testDetails!.length} ${LocaleKeys.tests.tr(context: context)}",
backgroundColor: AppColors.successColor.withOpacity(0.1),
textColor: AppColors.successColor,
),
AppCustomChipWidget(
labelText: "${_appState.isArabic() ? group.isInOutPatientDescriptionN : group.isInOutPatientDescription}",
backgroundColor: AppColors.warningColorYellow.withOpacity(0.1),
textColor: AppColors.warningColorYellow,
)
],
),
Icon(isExpanded ? Icons.expand_less : Icons.expand_more),
],
),
SizedBox(height: 8.h),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
group.doctorImageURL ?? "https://hmgwebservices.com/Images/MobileImages/DUBAI/unkown_female.png",
width: 24.w,
height: 24.h,
fit: BoxFit.cover,
).circle(100),
SizedBox(width: 8.h),
Expanded(child: (group.doctorName ?? group.doctorNameEnglish ?? "").toString().toText14(isBold: true)),
],
),
SizedBox(height: 8.h),
Wrap(
direction: Axis.horizontal,
spacing: 4.h,
runSpacing: 4.h,
children: [
Directionality(
textDirection: ui.TextDirection.ltr,
child: AppCustomChipWidget(
labelText: DateUtil.formatDateToDate(DateUtil.convertStringToDate(group.orderDate ?? ""), false),
isEnglishOnly: true,
)),
AppCustomChipWidget(
labelText: (group.projectName ?? ""),
),
AppCustomChipWidget(
labelText: (group.clinicDescription ?? ""),
),
],
),
],
),
),
AnimatedSwitcher(
duration: Duration(milliseconds: 500),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
transitionBuilder: (Widget child, Animation animation) {
return FadeTransition(
opacity: animation,
child: SizeTransition(
sizeFactor: animation,
axisAlignment: 0.0,
child: child,
),
);
},
child: isExpanded
? Container(
key: ValueKey(index),
padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 0.h),
child: Column(
children: [
ListView.separated(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
padding: EdgeInsets.zero,
itemBuilder: (cxt, index) {
PatientLabOrdersResponseModel order = group;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
"• ${order.testDetails![index].description!}".toText14(isBold: true, isEnglishOnly: true),
SizedBox(height: 4.h),
(_appState.isArabic() ? order.testDetails![index].testDescriptionAr! : order.testDetails![index].testDescriptionEn!)
.toText12(isBold: true, color: AppColors.textColorLight, isEnglishOnly: true),
],
).paddingOnly(top: 16, bottom: 16);
},
separatorBuilder: (cxt, index) => Divider(color: AppColors.borderOnlyColor.withValues(alpha: 0.05), height: 1.h),
itemCount: group.testDetails!.length > 3 ? 3 : group.testDetails!.length),
SizedBox(height: 16.h),
CustomButton(
text: "${LocaleKeys.viewResults.tr()} (${group.testDetails!.length})",
onPressed: () {
labProvider.currentlySelectedPatientOrder = group;
labProvider.getPatientLabResultByHospital(group);
labProvider.getPatientSpecialResult(group);
Navigator.of(context).push(
CustomPageRoute(
page: LabResultByClinic(labOrder: group),
),
);
},
backgroundColor: AppColors.infoColor.withAlpha(20),
borderColor: AppColors.infoColor.withAlpha(0),
textColor: AppColors.infoColor,
fontSize: (isFoldable || isTablet) ? 12.f : 14.f,
isBold: true,
borderRadius: 12.r,
padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
height: 40.h,
iconSize: 14.h,
icon: AppAssets.view_report_icon,
iconColor: AppColors.infoColor,
),
SizedBox(height: 16.h),
],
),
)
: SizedBox.shrink(),
),
],
),
),
),
),
));
},
)
: Utils.getNoDataWidget(context, noDataText: LocaleKeys.noLabResults.tr(context: context)))
: // By Test or other tabs keep existing behavior
(labViewModel.isLabOrdersLoading)
? Column(
children: List.generate(
5,
(index) => LabResultItemView(
onTap: () {},
labOrder: null,
index: index,
isLoading: true,
)),
)
: AlphabeticScroll(
alpahbetsAvailable: labViewModel.filteredIndexedCharacterForUniqueTest,
details: labViewModel.filteredUniqueTestsList,
labViewModel: labViewModel,
rangeViewModel: rangeViewModel,
appState: _appState,
)
],
));
},
),
);
}
Color getLabOrderStatusColor(num status) {
switch (status) {
case 44:
return AppColors.warningColorYellow;
case 45:
return AppColors.warningColorYellow;
case 16:
return AppColors.successColor;
case 17:
return AppColors.successColor;
default:
return AppColors.greyColor;
}
}
String getLabOrderStatusText(num status) {
switch (status) {
case 44:
return LocaleKeys.resultsPending.tr(context: context);
case 45:
return LocaleKeys.resultsPending.tr(context: context);
case 16:
return LocaleKeys.resultsAvailable.tr(context: context);
case 17:
return LocaleKeys.resultsAvailable.tr(context: context);
default:
return "";
}
}
getLabSuggestions(LabViewModel model) {
if (model.patientLabOrders.isEmpty) {
return [];
}
return model.patientLabOrders.map((m) => m.testDetails).toList();
}
}
\ No newline at end of file
diff --git a/lib/presentation/medical_file/medical_file_page.dart b/lib/presentation/medical_file/medical_file_page.dart
index 27978723..629ff15c 100644
--- a/lib/presentation/medical_file/medical_file_page.dart
+++ b/lib/presentation/medical_file/medical_file_page.dart
@@ -340,7 +340,7 @@ class _MedicalFilePageState extends State {
}
},
backgroundColor: insuranceVM.isInsuranceExpired
- ? AppColors.primaryRedColor.withOpacity(0.1)
+ ? AppColors.secondaryLightRedColor
: insuranceVM.isInsuranceActive
? AppColors.successColor.withOpacity(0.1)
: AppColors.warningColorYellow.withOpacity(0.1),
diff --git a/lib/presentation/my_family/my_family.dart b/lib/presentation/my_family/my_family.dart
index 4b6f820a..76f318f7 100644
--- a/lib/presentation/my_family/my_family.dart
+++ b/lib/presentation/my_family/my_family.dart
@@ -34,14 +34,13 @@ class _FamilyMedicalScreenState extends State {
super.initState();
medicalVM = context.read();
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 {
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 {
),
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 {
}) {
switch (index) {
case 0:
-
return FamilyCards(
profiles: patientFiles,
onSelect: (FamilyFileResponseModelLists profile) {
diff --git a/lib/presentation/my_family/widget/family_cards.dart b/lib/presentation/my_family/widget/family_cards.dart
index 0567e451..a52f2ca6 100644
--- a/lib/presentation/my_family/widget/family_cards.dart
+++ b/lib/presentation/my_family/widget/family_cards.dart
@@ -165,67 +165,63 @@ class _FamilyCardsState extends State {
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 {
},
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 {
],
),
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(),
],
],
),
diff --git a/lib/presentation/my_invoices/my_invoices_list.dart b/lib/presentation/my_invoices/my_invoices_list.dart
index 67ee05a6..95ff3e16 100644
--- a/lib/presentation/my_invoices/my_invoices_list.dart
+++ b/lib/presentation/my_invoices/my_invoices_list.dart
@@ -122,8 +122,8 @@ class _MyInvoicesListState extends State {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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) {
diff --git a/lib/presentation/onboarding/onboarding_screen.dart b/lib/presentation/onboarding/onboarding_screen.dart
index 0af63b40..1c5b64d8 100644
--- a/lib/presentation/onboarding/onboarding_screen.dart
+++ b/lib/presentation/onboarding/onboarding_screen.dart
@@ -111,7 +111,7 @@ class _OnboardingScreenState extends State {
onPressed: () => goToHomePage(),
width: 86.w,
height: 56.h,
- backgroundColor: Color(0xffFEE9EA),
+ backgroundColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
borderColor: Colors.transparent,
).paddingOnly(left: 24.w)
diff --git a/lib/presentation/onboarding/splash_animation_screen.dart b/lib/presentation/onboarding/splash_animation_screen.dart
index 105922bb..0fdd86a8 100644
--- a/lib/presentation/onboarding/splash_animation_screen.dart
+++ b/lib/presentation/onboarding/splash_animation_screen.dart
@@ -164,7 +164,7 @@ class _AnimatedScreenState extends State 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 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 with TickerProviderSt
width: 100,
height: 150,
decoration: BoxDecoration(
- color: const Color(0xffED1C2B),
+ color: AppColors.primaryRedColor,
borderRadius: BorderRadius.circular(330),
),
),
diff --git a/lib/presentation/prescriptions/prescription_item_view.dart b/lib/presentation/prescriptions/prescription_item_view.dart
index b0e11af0..59b10620 100644
--- a/lib/presentation/prescriptions/prescription_item_view.dart
+++ b/lib/presentation/prescriptions/prescription_item_view.dart
@@ -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,
diff --git a/lib/presentation/profile_settings/profile_settings.dart b/lib/presentation/profile_settings/profile_settings.dart
index 2e9bb3b7..ce300c9b 100644
--- a/lib/presentation/profile_settings/profile_settings.dart
+++ b/lib/presentation/profile_settings/profile_settings.dart
@@ -52,14 +52,13 @@ class ProfileSettings extends StatefulWidget {
}
class ProfileSettingsState extends State {
-
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 {
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 {
);
}
- 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 {
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 {
// 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 {
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 {
),
// 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(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 {
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;
diff --git a/lib/presentation/rate_appointment/rate_appointment_clinic.dart b/lib/presentation/rate_appointment/rate_appointment_clinic.dart
index c8aedea8..e285a7c1 100644
--- a/lib/presentation/rate_appointment/rate_appointment_clinic.dart
+++ b/lib/presentation/rate_appointment/rate_appointment_clinic.dart
@@ -159,9 +159,9 @@ class _RateAppointmentClinicState extends State {
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));
diff --git a/lib/presentation/rate_appointment/rate_appointment_doctor.dart b/lib/presentation/rate_appointment/rate_appointment_doctor.dart
index d78dc59e..b54b5741 100644
--- a/lib/presentation/rate_appointment/rate_appointment_doctor.dart
+++ b/lib/presentation/rate_appointment/rate_appointment_doctor.dart
@@ -181,9 +181,9 @@ class _RateAppointmentDoctorState extends State {
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);
},
diff --git a/lib/presentation/smartwatches/activity_detail.dart b/lib/presentation/smartwatches/activity_detail.dart
index 0c17a9c0..888533af 100644
--- a/lib/presentation/smartwatches/activity_detail.dart
+++ b/lib/presentation/smartwatches/activity_detail.dart
@@ -67,8 +67,8 @@ class _ActivityDetailsState extends State {
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"),
diff --git a/lib/presentation/smartwatches/health_dashboard/health_dashboard.dart b/lib/presentation/smartwatches/health_dashboard/health_dashboard.dart
index f30d87a9..84ec64e9 100644
--- a/lib/presentation/smartwatches/health_dashboard/health_dashboard.dart
+++ b/lib/presentation/smartwatches/health_dashboard/health_dashboard.dart
@@ -56,8 +56,8 @@ class _HealthDashboardState extends State 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),
diff --git a/lib/presentation/symptoms_checker/organ_selector_screen.dart b/lib/presentation/symptoms_checker/organ_selector_screen.dart
index 438d9728..5b5d8f2e 100644
--- a/lib/presentation/symptoms_checker/organ_selector_screen.dart
+++ b/lib/presentation/symptoms_checker/organ_selector_screen.dart
@@ -480,7 +480,7 @@ class _OrganSelectorPageState extends State 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 with SingleTicker
isDisabled: viewModel.selectedOrgans.isEmpty,
backgroundColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,
+ borderColor: Colors.transparent,
),
);
}
diff --git a/lib/presentation/symptoms_checker/widgets/selected_organs_section.dart b/lib/presentation/symptoms_checker/widgets/selected_organs_section.dart
index e2b342b6..a4f5f1ee 100644
--- a/lib/presentation/symptoms_checker/widgets/selected_organs_section.dart
+++ b/lib/presentation/symptoms_checker/widgets/selected_organs_section.dart
@@ -121,7 +121,7 @@ class _SelectedOrgansSectionState extends State {
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),
diff --git a/lib/presentation/symptoms_checker/widgets/triage_level_card.dart b/lib/presentation/symptoms_checker/widgets/triage_level_card.dart
index 19ad3935..8633d830 100644
--- a/lib/presentation/symptoms_checker/widgets/triage_level_card.dart
+++ b/lib/presentation/symptoms_checker/widgets/triage_level_card.dart
@@ -94,7 +94,7 @@ class _TriageLevelCardState extends State 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);
diff --git a/lib/presentation/todo_section/widgets/ancillary_orders_list.dart b/lib/presentation/todo_section/widgets/ancillary_orders_list.dart
index cc5bbbb6..da75dd7b 100644
--- a/lib/presentation/todo_section/widgets/ancillary_orders_list.dart
+++ b/lib/presentation/todo_section/widgets/ancillary_orders_list.dart
@@ -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,
diff --git a/lib/presentation/todo_section/widgets/ancillary_procedures_list.dart b/lib/presentation/todo_section/widgets/ancillary_procedures_list.dart
index 54c92789..b7f0f28a 100644
--- a/lib/presentation/todo_section/widgets/ancillary_procedures_list.dart
+++ b/lib/presentation/todo_section/widgets/ancillary_procedures_list.dart
@@ -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,
diff --git a/lib/presentation/water_monitor/widgets/cup_bottomsheet_widgets.dart b/lib/presentation/water_monitor/widgets/cup_bottomsheet_widgets.dart
index 058f14e8..aef892f9 100644
--- a/lib/presentation/water_monitor/widgets/cup_bottomsheet_widgets.dart
+++ b/lib/presentation/water_monitor/widgets/cup_bottomsheet_widgets.dart
@@ -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 {
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),
),
diff --git a/lib/presentation/weather/weather_details_page.dart b/lib/presentation/weather/weather_details_page.dart
index 8762a55a..50dbefa3 100644
--- a/lib/presentation/weather/weather_details_page.dart
+++ b/lib/presentation/weather/weather_details_page.dart
@@ -62,7 +62,7 @@ class _WeatherDetailsPageState extends State {
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 {
}
// 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')) {
diff --git a/lib/services/remote_config_service.dart b/lib/services/remote_config_service.dart
new file mode 100644
index 00000000..568bd4e6
--- /dev/null
+++ b/lib/services/remote_config_service.dart
@@ -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 initialize();
+
+ /// Fetches the latest values from Firebase (respecting the configured
+ /// minimum fetch interval) and activates them.
+ Future 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 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({occasionConfigKey: defaultJson});
+ } catch (ex) {
+ loggerService.logError('RemoteConfigService.initialize error: $ex');
+ }
+ }
+
+ @override
+ Future 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}';
+}
diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart
index 1ecbcb61..3b9cbdae 100644
--- a/lib/theme/colors.dart
+++ b/lib/theme/colors.dart
@@ -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 ─────────────────────────────────────────────��────────
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;
}
diff --git a/lib/widgets/appbar/collapsing_list_view.dart b/lib/widgets/appbar/collapsing_list_view.dart
index c3ebe4ba..0c677002 100644
--- a/lib/widgets/appbar/collapsing_list_view.dart
+++ b/lib/widgets/appbar/collapsing_list_view.dart
@@ -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 {
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 {
),
).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 {
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),
diff --git a/lib/widgets/bottom_navigation/bottom_navigation.dart b/lib/widgets/bottom_navigation/bottom_navigation.dart
index e999842f..a5b8b239 100644
--- a/lib/widgets/bottom_navigation/bottom_navigation.dart
+++ b/lib/widgets/bottom_navigation/bottom_navigation.dart
@@ -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),
diff --git a/lib/widgets/buttons/custom_button.dart b/lib/widgets/buttons/custom_button.dart
index 82b62507..8b8e599a 100644
--- a/lib/widgets/buttons/custom_button.dart
+++ b/lib/widgets/buttons/custom_button.dart
@@ -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 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 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,
diff --git a/lib/widgets/date_range_selector/date_range_calender.dart b/lib/widgets/date_range_selector/date_range_calender.dart
index a544f010..28098824 100644
--- a/lib/widgets/date_range_selector/date_range_calender.dart
+++ b/lib/widgets/date_range_selector/date_range_calender.dart
@@ -163,7 +163,7 @@ class _DateRangeSelectorState extends State {
),
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 {
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 {
}),
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 {
}),
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 {
],
),
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,
diff --git a/lib/widgets/language_switcher.dart b/lib/widgets/language_switcher.dart
index 15237363..7a556c84 100644
--- a/lib/widgets/language_switcher.dart
+++ b/lib/widgets/language_switcher.dart
@@ -56,21 +56,22 @@ class _LanguageSelectorState extends State {
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,
+ ),
],
),
),
diff --git a/lib/widgets/map/HMSMap.dart b/lib/widgets/map/HMSMap.dart
index 50da5f3a..e2059a52 100644
--- a/lib/widgets/map/HMSMap.dart
+++ b/lib/widgets/map/HMSMap.dart
@@ -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),
),
diff --git a/lib/widgets/map/gms_map.dart b/lib/widgets/map/gms_map.dart
index 33e0befb..07bf2674 100644
--- a/lib/widgets/map/gms_map.dart
+++ b/lib/widgets/map/gms_map.dart
@@ -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),
),
diff --git a/pubspec.lock b/pubspec.lock
index 2aeaaac9..70ddfc04 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -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:
diff --git a/pubspec.yaml b/pubspec.yaml
index 8cd63b67..c7e8ba1d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -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: