theme code updated.

pull/380/head
Sultan khan 1 month ago
parent 5cee0d4185
commit 8b9f4d98f3

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

@ -339,14 +339,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
@ -360,14 +356,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";

@ -5,7 +5,7 @@ import 'package:hmg_patient_app_new/core/enums.dart';
class ApiConsts {
static const maxSmallScreen = 660;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod;
static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.preProd;
// static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT

@ -84,7 +84,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';
@ -383,5 +385,13 @@ class AppDependencies {
getIt.registerLazySingleton<ClarityService>(
() => ClarityService(appState: getIt.get<AppState>()),
);
// Remote-config-driven "occasion" theming (National Day, Eid, etc.).
getIt.registerLazySingleton<RemoteConfigService>(() => RemoteConfigServiceImp(loggerService: getIt()));
getIt.registerLazySingleton<OccasionThemeViewModel>(() => OccasionThemeViewModel(
remoteConfigService: getIt(),
cacheService: getIt(),
loggerService: getIt(),
));
}
}

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

@ -0,0 +1,102 @@
import 'dart:convert';
import 'package:flutter/foundation.dart';
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;
/// Call once at app startup (before the first frame) to restore the last
/// known-good config instantly (offline-safe), then kick off a remote
/// fetch in the background.
Future<void> initialize() async {
_restoreCachedConfig();
_applyToAppColors();
try {
await _remoteConfigService.initialize();
await refresh();
} catch (ex) {
_loggerService.logError('OccasionThemeViewModel.initialize error: $ex');
}
}
/// Fetches the latest occasion config from Firebase Remote Config and
/// applies it. Safe to call again later (e.g. on app resume) to pick up
/// changes without requiring an app update.
Future<void> refresh() async {
try {
await _remoteConfigService.fetchAndActivate();
final rawJson = _remoteConfigService.occasionConfigRawJson;
_config = OccasionConfigModel.fromJson(json.decode(rawJson) as Map<String, dynamic>);
await _cacheService.saveString(key: _cachedConfigKey, value: rawJson);
_applyToAppColors();
notifyListeners();
} catch (ex) {
_loggerService.logError('OccasionThemeViewModel.refresh error: $ex');
// Keep whatever config was already applied (cached/disabled) — never
// let a remote-config failure change the app's appearance unexpectedly.
}
}
void _restoreCachedConfig() {
try {
final cachedJson = _cacheService.getString(key: _cachedConfigKey);
if (cachedJson == null || cachedJson.isEmpty) {
_config = OccasionConfigModel.disabled();
return;
}
_config = OccasionConfigModel.fromJson(json.decode(cachedJson) as Map<String, dynamic>);
} catch (ex) {
_loggerService.logError('OccasionThemeViewModel._restoreCachedConfig error: $ex');
_config = OccasionConfigModel.disabled();
}
}
/// Keeps AppColors in sync so non-Provider call sites (AppColors.primaryRedColor)
/// pick up the occasion color exactly like the existing dark-mode flag does.
void _applyToAppColors() {
AppColors.isOccasionThemeActive = isOccasionActive;
AppColors.occasionPrimaryColor = _config.primaryColor;
}
}

@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
@ -36,6 +37,7 @@ import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_reg
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/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';
@ -166,6 +168,14 @@ Future<void> callInitializations() async {
// Restore persisted accessibility settings before the first frame.
getIt.get<ProfileSettingsViewModel>().loadAccessibilitySettings();
// Restore last-known occasion theme instantly (offline-safe, synchronous)
// so the very first frame is already correct, then let the Remote Config
// fetch happen in the background without blocking app startup. Fully
// disabled by default — the app looks exactly as it does today until
// Remote Config enables an occasion (e.g. National Day) on the server side.
final occasionThemeViewModel = getIt.get<OccasionThemeViewModel>();
unawaited(occasionThemeViewModel.initialize());
}
void main() async {
@ -305,7 +315,10 @@ void main() async {
),
ChangeNotifierProvider<RefundRequestViewModel>(
create: (_) => getIt.get<RefundRequestViewModel>(),
)
),
ChangeNotifierProvider<OccasionThemeViewModel>(
create: (_) => getIt.get<OccasionThemeViewModel>(),
),
], child: MyApp()),
), // Wrap your app
);
@ -373,11 +386,11 @@ class MyApp extends StatelessWidget {
return Sizer(
builder: (context, orientation, deviceType) {
// SizeConfig().init(constraints, orientation);
return Consumer<ProfileSettingsViewModel>(
builder: (context, profileVm, _) {
return Consumer2<ProfileSettingsViewModel, OccasionThemeViewModel>(
builder: (context, profileVm, occasionThemeVm, _) {
final isArabic = EasyLocalization.of(context)?.locale.languageCode == "ar";
return MaterialApp(
key: ValueKey('app_theme_${profileVm.isDarkMode}'),
key: ValueKey('app_theme_${profileVm.isDarkMode}_${occasionThemeVm.isOccasionActive}_${occasionThemeVm.config.key}'),
// title: 'Dr. AlHabib',
title: 'Dr. AlHabib Beta',
builder: (context, mchild) {

@ -150,7 +150,7 @@ class AppointmentQueuePage extends StatelessWidget {
textColor: myAppointmentsVM.patientQueueDetailsList[index].callType == 1 ? AppColors.primaryRedColor : AppColors.successColor,
iconSize: 14.w,
backgroundColor: myAppointmentsVM.patientQueueDetailsList[index].callType == 1
? AppColors.primaryRedColor.withValues(alpha: 0.1)
? AppColors.primaryRedColorBase.withValues(alpha: 0.1)
: AppColors.successColor.withValues(alpha: 0.1),
labelPadding: EdgeInsetsDirectional.only(start: 8.h, end: -2.h),
),

@ -68,7 +68,7 @@ class AskDoctorAppointmentCard extends StatelessWidget {
),
AppCustomChipWidget(
labelText: LocaleKeys.outPatient.tr(context: context),
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.1),
textColor: AppColors.primaryRedColor,
),
AppCustomChipWidget(

@ -244,7 +244,7 @@ class _BloodDonationPageState extends State<BloodDonationPage> {
Container(
padding: EdgeInsets.all(8.h),
decoration: BoxDecoration(
color: AppColors.primaryRedColor.withValues(alpha: 0.1),
color: AppColors.primaryRedColorBase.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(8.r),
),
child: Utils.buildSvgWithAssets(

@ -59,7 +59,7 @@ class FeedbackPage extends StatelessWidget {
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
activeBackgroundColor: AppColors.primaryRedColorBase.withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, LocaleKeys.send.tr(context: context)),
CustomTabBarModel(null, LocaleKeys.status.tr(context: context)), //TODO: Need to be Disabled This Tab For Now

@ -44,7 +44,7 @@ class FindUsPage extends StatelessWidget {
? SizedBox.shrink()
: CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
activeBackgroundColor: AppColors.primaryRedColorBase.withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, LocaleKeys.hmgHospitals.tr()),
CustomTabBarModel(null, LocaleKeys.hmgPharmacy.tr()),

@ -667,7 +667,7 @@ class _ServicesPageState extends State<ServicesPage> {
borderWidth: 0.w,
isBold: true,
borderColor: Colors.transparent,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.08),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.08),
padding: EdgeInsets.all(8.w),
fontSize: 14.f,
onPressed: () {

@ -31,6 +31,7 @@ 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';
@ -49,6 +50,7 @@ import 'package:hmg_patient_app_new/presentation/home/data/landing_page_data.dar
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';
@ -232,6 +234,11 @@ class _LandingPageState extends State<LandingPage> {
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<OccasionThemeViewModel>().refresh());
if (appState.isAuthenticated) {
// Refresh Appointments Data
myAppointmentsViewModel.setIsAppointmentDataToBeLoaded(true);
@ -282,6 +289,9 @@ class _LandingPageState extends State<LandingPage> {
child: Column(
spacing: 16.h,
children: [
// Renders nothing (zero height) unless a remote-config
// occasion theme (e.g. National Day) is currently active.
const OccasionBannerWidget(),
Row(
spacing: 8.h,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -715,7 +725,7 @@ class _LandingPageState extends State<LandingPage> {
// // children: [
// // AppCustomChipWidget(
// // labelText: LocaleKeys.erOnlineCheckInRequest.tr(context: context),
// // backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.10),
// // backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.10),
// // textColor: AppColors.primaryRedColor,
// // ),
// // Utils.buildSvgWithAssets(icon: AppAssets.appointment_checkin_icon, width: 24.h, height: 24.h, iconColor: AppColors.primaryRedColor),
@ -1235,7 +1245,7 @@ class _LandingPageState extends State<LandingPage> {
iconColor: isVitalCall ? AppColors.primaryRedColor : AppColors.successColor,
textColor: isVitalCall ? AppColors.primaryRedColor : AppColors.successColor,
iconSize: 14.w,
backgroundColor: isVitalCall ? AppColors.primaryRedColor.withValues(alpha: 0.1) : AppColors.successColor.withValues(alpha: 0.1),
backgroundColor: isVitalCall ? AppColors.primaryRedColorBase.withValues(alpha: 0.1) : AppColors.successColor.withValues(alpha: 0.1),
labelPadding: EdgeInsetsDirectional.only(
start: 8.h,
end: -2.h,

@ -0,0 +1,61 @@
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/occasion_theme/occasion_theme_view_model.dart';
import 'package:provider/provider.dart';
/// Renders the remotely-configured occasion imagery:
/// - `background_opacity_image`: a decorative backdrop/watermark drawn at
/// [OccasionThemeViewModel.backgroundImageOpacity].
/// - `header_image`: the occasion's main header/banner (e.g. the National
/// Day greeting banner), drawn on top 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.
class OccasionBannerWidget extends StatelessWidget {
const OccasionBannerWidget({super.key});
@override
Widget build(BuildContext context) {
return Consumer<OccasionThemeViewModel>(
builder: (context, occasionThemeVm, _) {
final backgroundImageUrl = occasionThemeVm.backgroundOpacityImage;
final headerImageUrl = occasionThemeVm.headerImage;
final hasBackgroundImage = occasionThemeVm.isOccasionActive && (backgroundImageUrl?.isNotEmpty ?? false);
final hasHeaderImage = occasionThemeVm.isOccasionActive && (headerImageUrl?.isNotEmpty ?? false);
if (!hasBackgroundImage && !hasHeaderImage) {
return const SizedBox.shrink();
}
return Stack(
alignment: Alignment.center,
children: [
if (hasBackgroundImage)
Opacity(
opacity: occasionThemeVm.backgroundImageOpacity.clamp(0.0, 1.0),
child: CachedNetworkImage(
imageUrl: backgroundImageUrl!,
width: double.infinity,
fit: BoxFit.cover,
placeholder: (_, __) => const SizedBox.shrink(),
errorWidget: (_, __, ___) => const SizedBox.shrink(),
),
),
if (hasHeaderImage)
CachedNetworkImage(
imageUrl: headerImageUrl!,
width: double.infinity,
fit: BoxFit.cover,
placeholder: (_, __) => const SizedBox.shrink(),
errorWidget: (_, __, ___) => const SizedBox.shrink(),
),
],
);
},
);
}
}

@ -58,7 +58,7 @@ class InsuranceApprovalDetailsPage extends StatelessWidget {
? AppColors.successColor.withValues(alpha: 0.1)
: (insuranceApprovalResponseModel.approvalStatusDescption?.toLowerCase().contains('partial') == true || insuranceApprovalResponseModel.status == 8)
? AppColors.warningColorYellow.withValues(alpha: 0.1)
: AppColors.primaryRedColor.withValues(alpha: 0.1),
: AppColors.primaryRedColorBase.withValues(alpha: 0.1),
textColor: insuranceApprovalResponseModel.status == 9
? AppColors.successColor
: (insuranceApprovalResponseModel.approvalStatusDescption?.toLowerCase().contains('partial') == true || insuranceApprovalResponseModel.status == 8)

@ -37,7 +37,7 @@ class InsuranceApprovalCard extends StatelessWidget {
} else if (_isPartiallyApproved()) {
return AppColors.warningColorYellow.withValues(alpha: 0.1);
} else {
return AppColors.primaryRedColor.withValues(alpha: 0.1);
return AppColors.primaryRedColorBase.withValues(alpha: 0.1);
}
}
@ -126,7 +126,7 @@ class InsuranceApprovalCard extends StatelessWidget {
runSpacing: 4.h,
children: [
// AppCustomChipWidget(labelText: isLoading ? "Approved" : insuranceApprovalResponseModel.approvalStatusDescption!,
// backgroundColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor.withValues(alpha: 0.1) : AppColors.primaryRedColor.withValues(alpha: 0.1),
// backgroundColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor.withValues(alpha: 0.1) : AppColors.primaryRedColorBase.withValues(alpha: 0.1),
// textColor: insuranceApprovalResponseModel.status == 9 ? AppColors.successColor : AppColors.primaryRedColor,
// ).toShimmer2(isShow: isLoading),
AppCustomChipWidget(labelText: isLoading ? "Cardiology" : insuranceApprovalResponseModel.clinicName ?? "").toShimmer2(isShow: isLoading),

@ -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.primaryRedColorBase.withOpacity(0.1),
borderColor: AppColors.primaryRedColorBase.withOpacity(0.0),
textColor: AppColors.primaryRedColor,
fontSize: 10,
isBold: true,

@ -117,7 +117,7 @@ class PatientInsuranceCard extends StatelessWidget {
}
},
backgroundColor: isCurrentPatientInsuranceExpired(insuranceCardDetailsModel.cardValidTo!)
? AppColors.primaryRedColor.withOpacity(0.1)
? AppColors.primaryRedColorBase.withOpacity(0.1)
: 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),
// ),
],

@ -47,7 +47,7 @@ class EyeMeasurementDetailsPage extends StatelessWidget {
SizedBox(height: 16.h),
CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
activeBackgroundColor: AppColors.primaryRedColorBase.withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, LocaleKeys.classes.tr()),
CustomTabBarModel(null, LocaleKeys.contactLens.tr()),

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

@ -198,7 +198,7 @@ class _FamilyCardsState extends State<FamilyCards> {
backgroundColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor.withValues(alpha: 0.20)
? AppColors.primaryRedColorBase.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.active.toInt
? AppColors.lightGreenColor
: AppColors.lightGrayBGColor,
@ -410,7 +410,7 @@ class _FamilyCardsState extends State<FamilyCards> {
}
},
backgroundColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor.withValues(alpha: 0.1)
? AppColors.primaryRedColorBase.withValues(alpha: 0.1)
: insuranceVM.isInsuranceActive
? AppColors.successColor.withValues(alpha: 0.1)
: AppColors.warningColorYellow.withValues(alpha: 0.1),
@ -575,7 +575,7 @@ class _FamilyCardsState extends State<FamilyCards> {
backgroundColor: profile.status == FamilyFileEnum.pending.toInt
? AppColors.alertLightColor.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.rejected.toInt
? AppColors.primaryRedColor.withValues(alpha: 0.20)
? AppColors.primaryRedColorBase.withValues(alpha: 0.20)
: profile.status == FamilyFileEnum.active.toInt
? AppColors.lightGreenColor
: AppColors.lightGrayBGColor,

@ -201,8 +201,8 @@ class InvoiceListCard extends StatelessWidget {
onTap!();
}
},
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.1),
borderColor: AppColors.primaryRedColorBase.withValues(alpha: 0.01),
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,
@ -223,7 +223,7 @@ class InvoiceListCard extends StatelessWidget {
// icon: AppAssets.refundIcon,
// iconColor: AppColors.caloriesCalculatorColor,
// backgroundColor: AppColors.blueColor.withValues(alpha: 0.14),
// borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01),
// borderColor: AppColors.primaryRedColorBase.withValues(alpha: 0.01),
// textColor: AppColors.caloriesCalculatorColor,
// fontSize: 14.f,
// fontWeight: FontWeight.w600,

@ -57,7 +57,7 @@ class PharmacyInvoiceCard extends StatelessWidget {
// if (pharmacyInvoice.totalAmount != null)
// AppCustomChipWidget(
// labelText: '${pharmacyInvoice.totalAmount!.toStringAsFixed(2)} ${LocaleKeys.sar.tr(context: context)}',
// backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
// backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.1),
// textColor: AppColors.primaryRedColor,
// isEnglishOnly: true,
// ),
@ -158,8 +158,8 @@ class PharmacyInvoiceCard extends StatelessWidget {
? LocaleKeys.sendEmail.tr(context: context)
: LocaleKeys.downloadInvoice.tr(context: context),
onPressed: onTap,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.1),
borderColor: AppColors.primaryRedColorBase.withValues(alpha: 0.01),
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,

@ -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,

@ -701,7 +701,7 @@ class _FamilyCardWidgetState extends State<FamilyCardWidget> {
}
},
backgroundColor: insuranceVM.isInsuranceExpired
? AppColors.primaryRedColor.withValues(alpha: 0.1)
? AppColors.primaryRedColorBase.withValues(alpha: 0.1)
: insuranceVM.isInsuranceActive
? AppColors.successColor.withValues(alpha: 0.1)
: AppColors.warningColorYellow.withValues(alpha: 0.1),

@ -123,7 +123,7 @@ class FamilyCardWidget extends StatelessWidget {
icon = AppAssets.cancel_circle_icon;
labelText = LocaleKeys.insuranceExpired.tr(context: context);
iconColor = AppColors.primaryRedColor;
backgroundColor = AppColors.primaryRedColor.withValues(alpha: 0.15);
backgroundColor = AppColors.primaryRedColorBase.withValues(alpha: 0.15);
} else {
icon = AppAssets.insurance_active_icon;
labelText = LocaleKeys.insuranceActive.tr(context: context);

@ -173,8 +173,8 @@ class _RefundHistoryScreenState extends State<RefundHistoryScreen> {
CustomButton(
text: LocaleKeys.viewDetails.tr(context: context),
onPressed: () => Navigator.of(context).push(CustomPageRoute(page: RefundRequestDetailScreen(invoice: invoice!))),
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.1),
borderColor: AppColors.primaryRedColor.withValues(alpha: 0.01),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.1),
borderColor: AppColors.primaryRedColorBase.withValues(alpha: 0.01),
textColor: AppColors.primaryRedColor,
fontSize: 14.f,
fontWeight: FontWeight.w600,

@ -289,7 +289,7 @@ class _RiskFactorsScreenState extends State<RiskFactorsScreen> {
child: CustomButton(
text: LocaleKeys.previous.tr(context: context),
onPressed: _onPreviousPressed,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.11),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.11),
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,

@ -268,7 +268,7 @@ class _SuggestionsScreenState extends State<SuggestionsScreen> {
child: CustomButton(
text: LocaleKeys.previous.tr(),
onPressed: _onPreviousPressed,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.11),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.11),
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,

@ -360,7 +360,7 @@ class _SymptomsSelectorPageState extends State<SymptomsSelectorPage> {
child: CustomButton(
text: LocaleKeys.previous.tr(context: context),
onPressed: _onPreviousPressed,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.11),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.11),
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,

@ -750,7 +750,7 @@ class _TriagePageState extends State<TriagePage> {
percentage: probability.toInt(),
paddingBetween: 5.h,
color: AppColors.primaryRedColor,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.17),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.17),
height: 8.h,
titleWidget: RichText(
text: TextSpan(
@ -785,7 +785,7 @@ class _TriagePageState extends State<TriagePage> {
text: LocaleKeys.previous.tr(context: context),
onPressed: _onPreviousPressed,
isDisabled: viewModel.isTriageDiagnosisLoading,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.11),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.11),
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,

@ -148,7 +148,7 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
boxShadow: isCurrentStep && value > 0
? [
BoxShadow(
color: AppColors.primaryRedColor.withValues(alpha: 0.4),
color: AppColors.primaryRedColorBase.withValues(alpha: 0.4),
blurRadius: 8.r,
spreadRadius: 1.r,
),
@ -191,8 +191,8 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
isDisabled: !(viewModel.isCurrentUserInfoPageValid),
onPressed: () => viewModel.isCurrentUserInfoPageValid ? _onNext() : () {},
backgroundColor:
viewModel.isCurrentUserInfoPageValid ? AppColors.primaryRedColor : AppColors.primaryRedColor.withValues(alpha: 0.5),
borderColor: viewModel.isCurrentUserInfoPageValid ? AppColors.primaryRedColor : AppColors.primaryRedColor.withValues(alpha: 0.5),
viewModel.isCurrentUserInfoPageValid ? AppColors.primaryRedColor : AppColors.primaryRedColorBase.withValues(alpha: 0.5),
borderColor: viewModel.isCurrentUserInfoPageValid ? AppColors.primaryRedColor : AppColors.primaryRedColorBase.withValues(alpha: 0.5),
textColor: AppColors.whiteColor,
fontSize: 16.f,
)
@ -204,7 +204,7 @@ class _UserInfoFlowManagerState extends State<UserInfoFlowManager> {
child: CustomButton(
text: LocaleKeys.previous.tr(context: context),
onPressed: _onPrevious,
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.11),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.11),
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,

@ -296,7 +296,7 @@ class _UserInfoSelectionScreenState extends State<UserInfoSelectionScreen> {
context.read<SymptomsCheckerViewModel>().setUserInfoPage(0, isSinglePageEdit: false);
context.navigateWithName(AppRoutes.userInfoFlowManager);
},
backgroundColor: AppColors.primaryRedColor.withValues(alpha: 0.11),
backgroundColor: AppColors.primaryRedColorBase.withValues(alpha: 0.11),
borderColor: Colors.transparent,
textColor: AppColors.primaryRedColor,
fontSize: 16.f,

@ -188,7 +188,7 @@ class _PinchZoomTutorialOverlayState extends State<PinchZoomTutorialOverlay> wit
width: 48.w,
height: 48.h,
decoration: BoxDecoration(
color: AppColors.primaryRedColor.withValues(alpha: 0.1),
color: AppColors.primaryRedColorBase.withValues(alpha: 0.1),
borderRadius: BorderRadius.circular(12.r),
),
child: Icon(

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

@ -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.primaryRedColorBase.withOpacity(0.08) : Colors.transparent,
borderRadius: BorderRadius.circular(12.r),
border: Border.all(
color: isSelected ? AppColors.primaryRedColor : AppColors.bgScaffoldColor,
@ -271,9 +271,9 @@ class _CustomizeCupBottomSheetState extends State<CustomizeCupBottomSheet> {
SliderTheme(
data: SliderTheme.of(context).copyWith(
activeTrackColor: AppColors.primaryRedColor,
inactiveTrackColor: AppColors.primaryRedColor.withOpacity(0.2),
inactiveTrackColor: AppColors.primaryRedColorBase.withOpacity(0.2),
thumbColor: AppColors.primaryRedColor,
overlayColor: AppColors.primaryRedColor.withOpacity(0.2),
overlayColor: AppColors.primaryRedColorBase.withOpacity(0.2),
trackHeight: 4.h,
thumbShape: RoundSliderThumbShape(enabledThumbRadius: 10.w),
),

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

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

@ -5,6 +5,15 @@ 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;
static const transparent = Colors.transparent;
// ── Scaffold / Background ─────────────────────────────────────────────────
@ -20,7 +29,20 @@ class AppColors {
// ── 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);
/// 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);
static Color get secondaryLightRedColor => isDarkMode ? dark.secondaryLightRedColor : const Color(0xFFFEE9EA);
static const Color successColor = Color(0xFF18C273);
static Color get successLightBgColor => isDarkMode ? dark.successLightBgColor : const Color(0xffDDF6EA);

@ -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),

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

@ -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.primaryRedColorBase.withOpacity(0.1)
: Colors.transparent,
borderRadius: BorderRadius.circular(8.r),
),

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

@ -71,6 +71,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

Loading…
Cancel
Save