Compare commits

...

10 Commits

@ -778,13 +778,13 @@
"aboutApp": "حول التطبيق",
"dontHaveAccount": "ليس لديك حساب؟",
"loginOrRegister": "تسجيل الدخول أو التسجيل",
"myFiles" : "ملفاتي",
"myFiles": "ملفاتي",
"resultsPending": "النتائج معلقة",
"resultsAvailable": "النتائج متاحة",
"viewReport": "عرض التقرير",
"checkAvailability": "التحقق من التوفر",
"readInstructions": "قراءة التعليمات",
"searchLabReport" : "ابحث عن تقرير المختبر",
"searchLabReport": "ابحث عن تقرير المختبر",
"prescriptionDeliveryError": "هذه العيادة لا تدعم إعادة التعبئة والتسليم.",
"receiveOtpToast": "أين تود تلقي رمز التحقق OTP؟",
"enterPhoneNumber": "أدخل رقم الهاتف",
@ -807,10 +807,10 @@
"loginByOTP": "تسجيل الدخول بواسطة OTP",
"guest": "زائر",
"switchAccount": "تبديل الحساب",
"lastLoginBy":"آخر تسجيل دخول بواسطة",
"lastLoginBy": "آخر تسجيل دخول بواسطة",
"allSet": "جاهز! الآن يمكنك تسجيل الدخول باستخدام Face ID / Biometric أو البصمة",
"enableQuickLogin":"تمكين تسجيل الدخول السريع",
"enableMsg":"تمكين تسجيل الدخول السريع سيسمح بالتحقق من خلال Face ID / Biometric الخاص بجهازك الحالي",
"enableQuickLogin": "تمكين تسجيل الدخول السريع",
"enableMsg": "تمكين تسجيل الدخول السريع سيسمح بالتحقق من خلال Face ID / Biometric الخاص بجهازك الحالي",
"notNow": "ليس الآن",
"pendingActivation": "في انتظار التنشيط",
"awaitingApproval": "انتظر القبول",
@ -825,6 +825,27 @@
"selectFacility": "اختر المرافق",
"selectFacilitiesSubTitle": "يرجى اختيار المرفق للموعد",
"selectHospitalSubTitle": "يرجى اختيار المستشفى للموعد",
"iAcceptThe" : "أوافق على",
"iAcceptThe": "أوافق على",
"personalDetailsVerification": "التحقق من التفاصيل الشخصية",
"otpVerification": "التحقق من OTP",
"weHaveSendOTP": "لقد أرسلنا OTP إلى",
"via": "عبر",
"forRegistrationVerification": "للتحقق من التسجيل",
"didntReceiveIt": "لم تستلمه؟",
"resendOTP": "إعادة إرسال",
"resendIn": "إعادة الإرسال في",
"pleaseEnterAnationalID": "يرجى إدخال رقم الهوية الوطنية",
"pleaseEnterAFileNumber": "يرجى إدخال رقم الملف",
"pleaseEnterAValidEmail": "يرجى إدخال بريد إلكتروني صالح",
"pleaseEnterFullName": "يرجى إدخال الاسم الكامل",
"pleaseAcceptTermsConditions": "يرجى قبول الشروط والأحكام",
"pleaseEnterAValidIqamaID": "يرجى إدخال رقم إقامة صالح",
"pleaseEnterAValidNationalID": "يرجى إدخال رقم هوية وطنية صالح",
"pleaseEnterAValidDateOfBirth": "يرجى إدخال تاريخ ميلاد صالح",
"pleaseEnterAValidName": "يرجى إدخال اسم صالح",
"pleaseSelectAGender": "يرجى اختيار الجنس",
"pleaseSelectAMaritalStatus": "يرجى اختيار الحالة الاجتماعية",
"pleaseSelectACountry": "يرجى اختيار الدولة",
"pleaseEnterEmail": "يرجى إدخال البريد الإلكتروني",
"pleaseEnterAValidEmailFormat": "يرجى إدخال تنسيق بريد إلكتروني صالح"
}

@ -785,7 +785,7 @@
"viewReport": "View Report",
"checkAvailability": "Check Availability",
"readInstructions": "Read Instructions",
"searchLabReport" : "Search Lab Report",
"searchLabReport": "Search Lab Report",
"prescriptionDeliveryError": "This clinic doesn't support refill",
"prepareToElevate": "Prepared to elevate your health and well-being?",
"iAcceptTermsConditions": "I Accept the Terms and Conditions",
@ -797,7 +797,7 @@
"sendOTPSMS": "Send me OTP on SMS",
"fullName": "Full Name",
"married": "Married",
"uae" : "United Arab Emirates",
"uae": "United Arab Emirates",
"malE": "Male",
"loginBy": "Login By",
"loginByOTP": "Login By OTP",
@ -813,15 +813,36 @@
"enterValidNationalId": "Please enter a valid national ID or file number",
"enterValidPhoneNumber": "Please enter a valid phone number",
"ready": "Ready",
"medicalCentersWithCount" : "{count} Medical Centers",
"medicalCenters" : " Medical Centers",
"hospitalsWithCount" : "{count} Hospitals",
"medicalCentersWithCount": "{count} Medical Centers",
"medicalCenters": " Medical Centers",
"hospitalsWithCount": "{count} Hospitals",
"selectRegion": "Select Region",
"selectFacility": "Select Facilities",
"selectFacilitiesSubTitle": "Please select the facility for the appointment",
"selectHospitalSubTitle": "Please select the hospital for the appointment",
"news": "News",
"iAcceptThe" : "I Accept the",
"personalDetailsVerification": "Personal Details Verification"
"iAcceptThe": "I Accept the",
"personalDetailsVerification": "Personal Details Verification",
"otpVerification": "OTP Verification",
"weHaveSendOTP": "We have sent you the OTP code on",
"via": "via",
"forRegistrationVerification": "for registration verification",
"didntReceiveIt": "Didn't receive it?",
"resendOTP": "Resend",
"resendIn": "resend in",
"pleaseEnterAnationalID": "Please enter a national ID",
"pleaseEnterAFileNumber": "Please enter a file number",
"pleaseEnterAValidEmail": "Please enter a valid email",
"pleaseEnterFullName": "Please enter full name",
"pleaseAcceptTermsConditions": "Please accept the terms and conditions",
"pleaseEnterAValidIqamaID": "Please enter a valid Iqama ID",
"pleaseEnterAValidNationalID": "Please enter a valid national ID",
"pleaseEnterAValidDateOfBirth": "Please enter a valid date of birth",
"pleaseEnterAValidName": "Please enter a valid name",
"pleaseSelectAGender": "Please select a gender",
"pleaseSelectAMaritalStatus": "Please select a marital status",
"pleaseSelectACountry": "Please select a country",
"pleaseEnterEmail": "Please enter email",
"pleaseEnterAValidEmailFormat": "Please enter a valid email format"
}

@ -212,7 +212,7 @@ var GET_QR_PARKING = 'Services/SWP.svc/REST/GetQRParkingByID';
//URL to get clinic list
var GET_CLINICS_LIST_URL = "Services/lists.svc/REST/GetClinicCentralized";
var GET_CLINICS_LIST_WRT_HOSPITAL_URL = "Services/Lists.svc/REST/GetClinicFromDoctorSchedule";
var GET_CLINICS_LIST_WRT_HOSPITAL_ID_URL = "Services/Lists.svc/REST/GetClinicFromDoctorSchedule";
//URL to get active appointment list
var GET_ACTIVE_APPOINTMENTS_LIST_URL = "Services/Doctors.svc/Rest/Dr_GetAppointmentActiveNumber";
@ -723,7 +723,7 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In
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

@ -94,7 +94,7 @@ extension LoginTypeExtension on LoginTypeEnum {
String get displayName {
AppState appState = getIt.get<AppState>();
bool isArabic = appState.getLanguageID() == "ar";
bool isArabic = appState.getLanguageID() == 1 ? true : false;
switch (this) {
case LoginTypeEnum.sms:
return isArabic ? 'رسالة نصية' : 'SMS';

@ -29,32 +29,32 @@ class ValidationUtils {
static bool isValidatedId({String? nationalId, required Function() onOkPress, CountryEnum? selectedCountry, bool? isTermsAccepted, String? dob}) {
bool isCorrectID = true;
if (nationalId == null || nationalId.isEmpty) {
_dialogService.showExceptionBottomSheet(message: "Please enter a national ID", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAnationalID.tr(), onOkPressed: onOkPress);
isCorrectID = false;
}
if (nationalId != null && nationalId.isNotEmpty && selectedCountry != null) {
if (selectedCountry == CountryEnum.saudiArabia) {
if (!validateIqama(nationalId)) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid Iqama ID", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidIqamaID.tr(), onOkPressed: onOkPress);
return false;
}
}
if (selectedCountry == CountryEnum.unitedArabEmirates) {
if (!validateUaeNationalId(nationalId)) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid national ID", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidNationalID.tr(), onOkPressed: onOkPress);
return false;
}
}
if (dob == null || dob.isEmpty) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid date of birth", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidDateOfBirth.tr(), onOkPressed: onOkPress);
return false;
}
if (isTermsAccepted != null && !isTermsAccepted) {
_dialogService.showExceptionBottomSheet(message: "Please accept the terms and conditions", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseAcceptTermsConditions.tr(), onOkPressed: onOkPress);
return false;
}
}
@ -63,7 +63,7 @@ class ValidationUtils {
static bool isValidatePhone({String? phoneNumber, required Function() onOkPress}) {
if (phoneNumber == null || phoneNumber.isEmpty) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid phone number", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.enterValidPhoneNumber.tr(), onOkPressed: onOkPress);
return false;
}
return true;
@ -71,7 +71,7 @@ class ValidationUtils {
static bool isValidate({String? phoneNumber, required Function() onOkPress}) {
if (phoneNumber == null || phoneNumber.isEmpty) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid phone number", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.enterValidPhoneNumber.tr(), onOkPressed: onOkPress);
return false;
}
return true;
@ -104,22 +104,22 @@ class ValidationUtils {
static bool validateUaeRegistration({String? name, GenderTypeEnum? gender, NationalityCountries? country, MaritalStatusTypeEnum? maritalStatus, required Function() onOkPress}) {
if (name == null || name.isEmpty) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid name", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidName.tr(), onOkPressed: onOkPress);
return false;
}
if (gender == null) {
_dialogService.showExceptionBottomSheet(message: "Please select a gender", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseSelectAGender.tr(), onOkPressed: onOkPress);
return false;
}
if (maritalStatus == null) {
_dialogService.showExceptionBottomSheet(message: "Please select a marital status", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseSelectAMaritalStatus.tr(), onOkPressed: onOkPress);
return false;
}
if (country == null) {
_dialogService.showExceptionBottomSheet(message: "Please select a country", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseSelectACountry.tr(), onOkPressed: onOkPress);
return false;
}
@ -128,13 +128,13 @@ class ValidationUtils {
static bool isValidateEmail({String? email, required Function() onOkPress}) {
if (email == null || email.isEmpty) {
_dialogService.showExceptionBottomSheet(message: "Please enter email", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterEmail.tr(), onOkPressed: onOkPress);
return false;
}
final bool emailIsValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,}$").hasMatch(email);
if (!emailIsValid) {
_dialogService.showExceptionBottomSheet(message: "Please enter a valid email format", onOkPressed: onOkPress);
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidEmailFormat.tr(), onOkPressed: onOkPress);
return false;
}

@ -381,7 +381,7 @@ class FontUtils {
/// Get the appropriate font family for a specific language
static String getFontFamilyForLanguage(bool isArabic) {
return isArabic ? 'Cairo' : 'Poppins';
return isArabic ? 'GESSTwo' : 'Poppins';
}
}

@ -1,5 +1,6 @@
import 'dart:convert';
import 'dart:developer';
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:flutter/material.dart';
@ -36,6 +37,7 @@ import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart';
import 'package:hmg_patient_app_new/widgets/bottomsheet/exception_bottom_sheet.dart';
import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';
import 'models/request_models/get_user_mobile_device_data.dart';
import 'models/request_models/insert_patient_mobile_deviceinfo.dart';
@ -363,7 +365,7 @@ class AuthenticationViewModel extends ChangeNotifier {
);
// TODO: GET APP SMS SIGNATURE HERE
request.sMSSignature = "enKTDcqbOVd";
request.sMSSignature = await getSignature();
if (checkIsUserComingForRegister(request: payload)) {
_appState.setUserRegistrationPayload = RegistrationDataModelPayload.fromJson(payload);
@ -619,8 +621,8 @@ class AuthenticationViewModel extends ChangeNotifier {
} else {
// authenticated = true;
await insertPatientIMEIData(loginTypeEnum.toInt);
}
LoaderBottomSheet.hideLoader();
}
notifyListeners();
// navigateToHomeScreen();
} else {
@ -875,6 +877,7 @@ class AuthenticationViewModel extends ChangeNotifier {
getDeviceLastLogin = deviceInfo.first['LoginType'];
await checkActivationCode(otpTypeEnum: OTPTypeEnum.faceIDFingerprint, activationCode: null, onWrongActivationCode: (String? message) {});
await insertPatientIMEIData(loginTypeEnum.toInt);
LoaderBottomSheet.hideLoader();
}
if (apiResponse.messageStatus == 2) {
LoaderBottomSheet.hideLoader();
@ -940,5 +943,11 @@ class AuthenticationViewModel extends ChangeNotifier {
);
}
// === OTP Widget Logics ===//
Future<String?> getSignature() async {
if (Platform.isAndroid) {
return await SmsVerification.getAppSignature();
} else {
return null;
}
}
}

@ -1,15 +1,25 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/animation.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
import 'package:get_it/get_it.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/utils/size_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/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/services/cache_service.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/app_bar_widget.dart';
import 'package:sms_otp_auto_verify/sms_otp_auto_verify.dart';
import 'package:provider/provider.dart';
typedef OnDone = void Function(String text);
@ -49,7 +59,8 @@ class OTPWidget extends StatefulWidget {
final FocusNode? focusNode;
final AnimatedSwitcherTransitionBuilder? pinTextAnimatedSwitcherTransition;
final Duration pinTextAnimatedSwitcherDuration;
final TextDirection textDirection;
// final TextDirection textDirection;
final TextInputType keyboardType;
final EdgeInsets pinBoxOuterPadding;
@ -70,7 +81,6 @@ class OTPWidget extends StatefulWidget {
this.onTextChanged,
this.autoFocus = false,
this.focusNode,
this.textDirection = TextDirection.ltr,
this.keyboardType = TextInputType.number,
this.pinBoxOuterPadding = const EdgeInsets.symmetric(horizontal: 4.0),
this.pinBoxColor = Colors.white,
@ -94,6 +104,8 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
bool hasFocus = false;
AuthenticationViewModel? authVm;
final CacheService cacheService = GetIt.instance<CacheService>();
@override
void didUpdateWidget(OTPWidget oldWidget) {
super.didUpdateWidget(oldWidget);
@ -137,6 +149,7 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
}
focusNode.addListener(_focusListener);
authVm?.otpScreenNotifier.addListener(_onOtpScreenNotifierChanged);
cacheService.remove(key: CacheConst.quickLoginEnabled);
}
void _controllerListener() {
@ -175,17 +188,9 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
widget.controller!.clear();
}
// Remove focus from the input
if (focusNode.hasFocus) {
focusNode.unfocus();
}
// Optionally refocus after a short delay to allow user to re-enter OTP
Future.delayed(const Duration(milliseconds: 100), () {
if (mounted && widget.autoFocus) {
FocusScope.of(context).requestFocus(focusNode);
}
});
}
}
@ -395,58 +400,6 @@ class OTPWidgetState extends State<OTPWidget> with SingleTickerProviderStateMixi
);
}
// Widget _buildPinCode(int i, BuildContext context) {
// Color pinBoxColor = widget.pinBoxColor;
//
// if (widget.hasError) {
// pinBoxColor = widget.errorBorderColor;
// } else if (i < text.length) {
// pinBoxColor = AppColors.blackBgColor; // Custom color for filled boxes
// } else {
// pinBoxColor = widget.pinBoxColor;
// }
//
// // Change color to success when all fields are complete
// if (text.length == widget.maxLength) {
// pinBoxColor = AppColors.successColor;
// }
//
// EdgeInsets insets;
// if (i == 0) {
// insets = EdgeInsets.only(
// left: 0,
// top: widget.pinBoxOuterPadding.top,
// right: widget.pinBoxOuterPadding.right,
// bottom: widget.pinBoxOuterPadding.bottom,
// );
// } else if (i == strList.length - 1) {
// insets = EdgeInsets.only(
// left: widget.pinBoxOuterPadding.left,
// top: widget.pinBoxOuterPadding.top,
// right: 0,
// bottom: widget.pinBoxOuterPadding.bottom,
// );
// } else {
// insets = widget.pinBoxOuterPadding;
// }
//
// return AnimatedContainer(
// duration: const Duration(milliseconds: 200),
// curve: Curves.easeInOut,
// key: ValueKey<String>("container$i"),
// alignment: Alignment.center,
// padding: EdgeInsets.symmetric(vertical: 4.0, horizontal: 1.0),
// margin: insets,
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
// color: pinBoxColor,
// borderRadius: widget.pinBoxRadius,
// ),
// width: widget.pinBoxWidth,
// height: widget.pinBoxHeight,
// child: _animatedTextBox(strList[i], i),
// );
// }
Widget _animatedTextBox(String text, int i) {
if (widget.pinTextAnimatedSwitcherTransition != null) {
return AnimatedSwitcher(
@ -501,6 +454,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
super.initState();
_otpController = TextEditingController();
_startResendTimer();
checkSignature();
}
@override
@ -534,6 +488,30 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
}
}
void checkSignature() async {
SmsVerification.startListeningSms().then((message) {
final intRegex = RegExp(r'\d+', multiLine: true);
var otp = SmsVerification.getCode(message, intRegex);
if (otp != null && otp.length == _otpLength) {
autoFillOtp(otp); // Use autoFillOtp to update controller and UI
}
SmsVerification.stopListening();
});
}
void _onAutoOtpChanged(String value) {
setState(() {
_isOtpComplete = value.length == _otpLength;
});
if (_isOtpComplete && !_isVerifying) {
_isVerifying = true;
_verifyOtp(value);
} else if (!_isOtpComplete) {
_isVerifying = false;
}
}
void _resendOtp() {
if (_resendTime == 0) {
setState(() {
@ -547,14 +525,6 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
}
}
void _clearOtpAndResetState() {
setState(() {
_isVerifying = false;
_isOtpComplete = false;
});
_otpController.clear();
}
String _getMaskedPhoneNumber() {
final phone = widget.phoneNumber;
return phone.length > 4 ? '05xxxxxx${phone.substring(phone.length - 2)}' : phone;
@ -562,6 +532,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
@override
Widget build(BuildContext context) {
AuthenticationViewModel authVM = context.read<AuthenticationViewModel>();
return Scaffold(
backgroundColor: AppColors.scaffoldBgColor,
appBar: CustomAppBar(
@ -577,19 +548,22 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 40.h),
Text(
'OTP Verification',
style: TextStyle(fontSize: 24.fSize, fontWeight: FontWeight.bold),
),
SizedBox(height: 16.h),
Text(
'We have sent you the OTP code on ${_getMaskedPhoneNumber()} via SMS for registration verification',
style: TextStyle(fontSize: 16.fSize, color: Colors.grey),
SizedBox(height: 10.h),
LocaleKeys.otpVerification.tr().toText24(isBold: true),
SizedBox(height: 20.h),
Wrap(
spacing: 4.h,
runSpacing: 8.0,
children: [
LocaleKeys.weHaveSendOTP.tr().toText16(color: AppColors.inputLabelTextColor),
_getMaskedPhoneNumber().toText16(color: AppColors.inputLabelTextColor, isBold: true),
LocaleKeys.via.tr().toText16(color: AppColors.inputLabelTextColor),
authVM.loginTypeEnum.displayName.toText16(color: AppColors.inputLabelTextColor),
LocaleKeys.forRegistrationVerification.tr().toText16(color: AppColors.inputLabelTextColor),
],
),
SizedBox(height: 40.h),
// OTP Input Fields using new OTP Widget
SizedBox(height: 16.h),
Center(
child: OTPWidget(
maxLength: _otpLength,
@ -611,38 +585,32 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
),
),
),
const SizedBox(height: 32),
SizedBox(height: 32.h),
// Resend OTP
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("Didn't receive it? "),
LocaleKeys.didntReceiveIt.tr().toText16(color: AppColors.inputLabelTextColor),
SizedBox(width: 5.h),
if (_resendTime > 0)
Builder(
// Use a Builder to easily calculate minutes and seconds inline
builder: (context) {
final minutes = (_resendTime ~/ 60)
.toString()
.padLeft(2, '0'); // Integer division for minutes final seconds = (_resendTime % 60).toString().padLeft(2, '0'); // Modulo for remaining seconds
final seconds = (_resendTime % 60).toString().padLeft(2, '0'); // Modulo for remaining seconds // <--- HERE IT IS
return Text(
'resend in ($minutes:$seconds). ',
style: const TextStyle(color: Colors.grey),
final minutes = (_resendTime ~/ 60).toString().padLeft(2, '0');
final seconds = (_resendTime % 60).toString().padLeft(2, '0');
return Row(
children: [
LocaleKeys.resendIn.tr().toText16(color: AppColors.inputLabelTextColor),
SizedBox(width: 2.h),
' ($minutes:$seconds). '.toText16(color: AppColors.inputLabelTextColor)
],
);
},
)
else
GestureDetector(
onTap: _resendOtp,
child: const Text(
'Resend',
style: TextStyle(
color: AppColors.primaryRedColor,
fontWeight: FontWeight.bold,
),
),
child: LocaleKeys.resendOTP.tr().toText16(color: AppColors.primaryRedColor),
),
],
),
@ -654,7 +622,6 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
}
void _verifyOtp(String otp) {
debugPrint('Verifying OTP: $otp');
widget.checkActivationCode(int.parse(otp));
}
@ -664,5 +631,7 @@ class _OTPVerificationScreenState extends State<OTPVerificationScreen> {
if (otp.length != _otpLength) return;
_isVerifying = false;
_otpController.text = otp;
setState(() {});
_onOtpChanged(otp);
}
}

@ -44,6 +44,9 @@ abstract class BookAppointmentsRepo {
Future<Either<Failure, GenericApiModel<List<HospitalsModel>>>>
getProjectList();
Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinicsWithRespectToClinicId(String projectID);
}
class BookAppointmentsRepoImp implements BookAppointmentsRepo {
@ -409,4 +412,42 @@ class BookAppointmentsRepoImp implements BookAppointmentsRepo {
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetClinicsListResponseModel>>>> getClinicsWithRespectToClinicId(String projectID) async {
Map<String, dynamic> mapDevice = {"ProjectID": projectID};
try {
GenericApiModel<List<GetClinicsListResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_CLINICS_LIST_WRT_HOSPITAL_ID_URL,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['ListClinic'];
final clinicsList = list.map((item) => GetClinicsListResponseModel.fromJson(item as Map<String, dynamic>)).toList().cast<GetClinicsListResponseModel>();
apiResponse = GenericApiModel<List<GetClinicsListResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: clinicsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -70,6 +70,10 @@ class BookAppointmentsViewModel extends ChangeNotifier {
FacilitySelection currentlySelectedFacility = FacilitySelection.ALL;
bool isRegionListLoading = false;
///this will be used to call the clinic call when navigating from my REGION SELECTION to book appointment screen
bool shouldLoadSpecificClinic = false;
String? currentlySelectedHospitalFromRegionFlow;
BookAppointmentsViewModel({required this.bookAppointmentsRepo, required this.errorHandlerService, required this.navigationService, required this.myAppointmentsViewModel});
void initializeFilteredList() {
@ -109,7 +113,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
clinicsList.clear();
}
isClinicsListLoading = value;
notifyListeners();
// notifyListeners();
}
setSelectedClinic(GetClinicsListResponseModel clinic) {
@ -148,7 +152,18 @@ class BookAppointmentsViewModel extends ChangeNotifier {
notifyListeners();
}
Future<void> getClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
/// this function will decide which clinic api to be called
/// either api for region flow or the select clinic api
Future<void> getClinics() async
{
if(shouldLoadSpecificClinic) {
getRegionSelectedClinics();
} else {
getAllClinics();
}
}
Future<void> getAllClinics({Function(dynamic)? onSuccess, Function(String)? onError}) async {
final result = await bookAppointmentsRepo.getClinics();
result.fold(
@ -173,6 +188,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
Future<void> getDoctorsList(
{int projectID = 0, bool isNearest = false, int doctorId = 0, String doctorName = "", isContinueDentalPlan = false, Function(dynamic)? onSuccess, Function(String)? onError}) async {
doctorsList.clear();
projectID = currentlySelectedHospitalFromRegionFlow != null?int.parse(currentlySelectedHospitalFromRegionFlow!):projectID;
final result = await bookAppointmentsRepo.getDoctorsList(selectedClinic.clinicID ?? 0, projectID, isNearest, doctorId, doctorName);
result.fold(
@ -391,6 +407,7 @@ class BookAppointmentsViewModel extends ChangeNotifier {
}
Future<void> getRegionMappedProjectList() async {
//todo handle the case in the location is switch on
if(hospitalList != null && hospitalList!.registeredDoctorMap != null && hospitalList!.registeredDoctorMap!.isNotEmpty){
filteredHospitalList = hospitalList;
return;
@ -469,4 +486,33 @@ class BookAppointmentsViewModel extends ChangeNotifier {
isLocationEnabled().then((value) => isLocationAvaiable = value);
return isLocationAvaiable;
}
void setLoadSpecificClinic(bool status) {
shouldLoadSpecificClinic = status;
}
void setProjectID(String? mainProjectID) {
currentlySelectedHospitalFromRegionFlow = mainProjectID;
}
Future<void> getRegionSelectedClinics() async{
final result = await bookAppointmentsRepo.getClinicsWithRespectToClinicId(currentlySelectedHospitalFromRegionFlow??"");
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) {
clinicsList = apiResponse.data!;
isClinicsListLoading = false;
initializeFilteredList();
notifyListeners();
}
},
);
}
void resetFilterList(){
filteredHospitalList = hospitalList;
}
}

@ -1,4 +1,8 @@
import 'package:flutter/foundation.dart' show ChangeNotifier;
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/doctor_list_api_response.dart';
import 'package:hmg_patient_app_new/presentation/book_appointment/select_clinic_page.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart';
enum AppointmentViaRegionState {
REGION_SELECTION,
@ -11,9 +15,13 @@ enum AppointmentViaRegionState {
class AppointmentViaRegionViewmodel extends ChangeNotifier {
String? selectedRegionId;
String? selectedFacilityType;
PatientDoctorAppointmentList? selectedHospital;
final NavigationService navigationService;
AppointmentViaRegionState bottomSheetState =
AppointmentViaRegionState.REGION_SELECTION;
AppointmentViaRegionViewmodel({required this.navigationService});
void setSelectedRegionId(String? regionId) {
selectedRegionId = regionId;
notifyListeners();
@ -29,10 +37,16 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
notifyListeners();
}
void handleLastStep(){
navigationService.pop();
navigationService.push(FadePage(
page: SelectClinicPage(),
),);
}
void handleBackPress() {
switch (bottomSheetState) {
case AppointmentViaRegionState.REGION_SELECTION:
// Do nothing or exit the bottom sheet
break;
case AppointmentViaRegionState.TYPE_SELECTION:
setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION);
@ -41,19 +55,6 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
case AppointmentViaRegionState.HOSPITAL_SELECTION:
setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION);
break;
// case AppointmentViaRegionState.HOSPITAL_SELECTION:
// case AppointmentViaRegionState.CLINIC_SELECTION:
// setBottomSheetState(AppointmentViaRegionState.TYPE_SELECTION);
// setFacility(null);
// break;
// case AppointmentViaRegionState.DOCTOR_SELECTION:
// if (selectedFacilityType == 'Hospital') {
// setBottomSheetState(AppointmentViaRegionState.HOSPITAL_SELECTION);
// } else if (selectedFacilityType == 'Medical Center') {
// setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);
// }
// break;
default:
}
}
@ -63,4 +64,8 @@ class AppointmentViaRegionViewmodel extends ChangeNotifier {
setFacility(null);
setBottomSheetState(AppointmentViaRegionState.REGION_SELECTION);
}
void setHospitalModel(PatientDoctorAppointmentList? hospital) {
selectedHospital = hospital;
}
}

@ -825,5 +825,26 @@ abstract class LocaleKeys {
static const selectHospitalSubTitle = 'selectHospitalSubTitle';
static const iAcceptThe = 'iAcceptThe';
static const personalDetailsVerification = 'personalDetailsVerification';
static const otpVerification = 'otpVerification';
static const weHaveSendOTP = 'weHaveSendOTP';
static const via = 'via';
static const forRegistrationVerification = 'forRegistrationVerification';
static const didntReceiveIt = 'didntReceiveIt';
static const resendOTP = 'resendOTP';
static const resendIn = 'resendIn';
static const pleaseEnterAnationalID = 'pleaseEnterAnationalID';
static const pleaseEnterAFileNumber = 'pleaseEnterAFileNumber';
static const pleaseEnterAValidEmail = 'pleaseEnterAValidEmail';
static const pleaseEnterFullName = 'pleaseEnterFullName';
static const pleaseAcceptTermsConditions = 'pleaseAcceptTermsConditions';
static const pleaseEnterAValidIqamaID = 'pleaseEnterAValidIqamaID';
static const pleaseEnterAValidNationalID = 'pleaseEnterAValidNationalID';
static const pleaseEnterAValidDateOfBirth = 'pleaseEnterAValidDateOfBirth';
static const pleaseEnterAValidName = 'pleaseEnterAValidName';
static const pleaseSelectAGender = 'pleaseSelectAGender';
static const pleaseSelectAMaritalStatus = 'pleaseSelectAMaritalStatus';
static const pleaseSelectACountry = 'pleaseSelectACountry';
static const pleaseEnterEmail = 'pleaseEnterEmail';
static const pleaseEnterAValidEmailFormat = 'pleaseEnterAValidEmailFormat';
}

@ -146,7 +146,8 @@ void main() async {
),
),
ChangeNotifierProvider<AppointmentViaRegionViewmodel>(
create: (_) => AppointmentViaRegionViewmodel())
create: (_) =>
AppointmentViaRegionViewmodel(navigationService: getIt()))
], child: MyApp()),
),
);

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/core/utils/size_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/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/appointment_via_region_viewmodel.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart';
@ -76,8 +77,9 @@ class HospitalBottomSheetBody extends StatelessWidget {
SizedBox(
height: MediaQuery.sizeOf(context).height * .4,
child: ListView.separated(
itemBuilder: (_, index) => HospitalListItem(
hospitalData: regionalViewModel.selectedFacilityType ==
itemBuilder: (_, index)
{
var hospital = regionalViewModel.selectedFacilityType ==
FacilitySelection.HMG.name
? appointmentsViewModel
.filteredHospitalList!
@ -88,9 +90,15 @@ class HospitalBottomSheetBody extends StatelessWidget {
.filteredHospitalList
?.registeredDoctorMap?[
regionalViewModel.selectedRegionId!]
?.hmcDoctorList?[index],
?.hmcDoctorList?[index];
return HospitalListItem(
hospitalData: hospital,
isLocationEnabled: appointmentsViewModel.getLocationStatus(),
),
).onPress(() {
regionalViewModel.setHospitalModel(hospital);
regionalViewModel.setBottomSheetState(AppointmentViaRegionState.CLINIC_SELECTION);
regionalViewModel.handleLastStep();
});},
separatorBuilder: (_, __) => SizedBox(
height: 16.h,
),

@ -93,7 +93,7 @@ class _RegisterNew extends State<RegisterNew> {
CustomCountryDropdown(
countryList: CountryEnum.values,
onCountryChange: authVm.onCountryChange,
isRtl: Directionality.of(context) == TextDirection.LTR,
// isRtl: Directionality.of(context) == TextDirection.LTR,
).withVerticalPadding(8.h),
Divider(height: 1.h),
TextInputWidget(
@ -189,7 +189,7 @@ class _RegisterNew extends State<RegisterNew> {
),
SizedBox(height: 25.h),
CustomButton(
text: "Register",
text: LocaleKeys.registernow.tr(),
icon: AppAssets.note_edit,
onPressed: () {
// Dismiss keyboard before proceeding

@ -43,6 +43,9 @@ class _RegisterNew extends State<RegisterNewStep2> {
@override
Widget build(BuildContext context) {
AppState appState = getIt.get<AppState>();
var name = appState.getLanguageCode() == "en"
? ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}")
: ("${appState.getNHICUserData.firstNameAr!.toUpperCase()} ${appState.getNHICUserData.lastNameAr!.toUpperCase()}");
return Scaffold(
backgroundColor: AppColors.bgScaffoldColor,
appBar: CustomAppBar(
@ -71,8 +74,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
children: [
TextInputWidget(
labelText: authVM!.isUserFromUAE() ? LocaleKeys.fullName.tr() : LocaleKeys.name.tr(),
hintText:
authVM!.isUserFromUAE() ? LocaleKeys.enterNameHere.tr() : ("${appState.getNHICUserData.firstNameEn!.toUpperCase()} ${appState.getNHICUserData.lastNameEn!.toUpperCase()}"),
hintText: authVM!.isUserFromUAE() ? LocaleKeys.enterNameHere.tr() : (name),
controller: authVM!.isUserFromUAE() ? authVM!.nameController : null,
isEnable: true,
prefix: null,
@ -332,7 +334,7 @@ class _RegisterNew extends State<RegisterNewStep2> {
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: CustomButton(
text: LocaleKeys.submit,
text: LocaleKeys.submit.tr(),
onPressed: () {
if (ValidationUtils.isValidateEmail(
email: authVM!.emailController.text,

@ -122,6 +122,8 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
],
).onPress(() {
bookAppointmentsViewModel.setIsClinicsListLoading(true);
bookAppointmentsViewModel.setLoadSpecificClinic(false);
bookAppointmentsViewModel.setProjectID(null);
Navigator.of(context).push(
FadePage(
page: SelectClinicPage(),
@ -194,6 +196,7 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
}
void openRegionListBottomSheet(BuildContext context) {
regionalViewModel.flush();
// AppointmentViaRegionViewmodel? viewmodel = null;
showCommonBottomSheetWithoutHeight(context,
title: "",
@ -203,7 +206,6 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
child: Consumer<AppointmentViaRegionViewmodel>(builder: (_, data, __) {
return getRegionalSelectionWidget(data);
}), callBackFunc: () {
regionalViewModel.flush();
});
}
@ -212,11 +214,20 @@ class _BookAppointmentPageState extends State<BookAppointmentPage> {
return RegionBottomSheetBody();
}
if(data.bottomSheetState == AppointmentViaRegionState.TYPE_SELECTION){
bookAppointmentsViewModel.resetFilterList();
return FacilityTypeSelectionWidget(selectedRegion: data.selectedRegionId??"",);
}
if (data.bottomSheetState == AppointmentViaRegionState.HOSPITAL_SELECTION) {
return HospitalBottomSheetBody();
} else {
}
if (data.bottomSheetState == AppointmentViaRegionState.CLINIC_SELECTION) {
// Navigator.of(context).pop();
bookAppointmentsViewModel.setIsClinicsListLoading(true);
bookAppointmentsViewModel.setLoadSpecificClinic(true);
bookAppointmentsViewModel.setProjectID(regionalViewModel.selectedHospital?.hospitalList.first?.mainProjectID.toString());
}
else {
SizedBox.shrink();
}
return SizedBox.shrink();

@ -93,6 +93,7 @@ class _LandingPageState extends State<LandingPage> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
appState.isAuthenticated
? WelcomeWidget(
onTap: () {

@ -27,7 +27,7 @@ class _LandingNavigationState extends State<LandingNavigation> {
physics: const NeverScrollableScrollPhysics(),
children: [
const LandingPage(),
appState.isAuthenticated ? MedicalFilePage() :/* need add feedback page */ const LandingPage(),
appState.isAuthenticated ? MedicalFilePage() : /* need add feedback page */ const LandingPage(),
BookAppointmentPage(),
const LandingPage(),
appState.isAuthenticated ? /* need add news page */ LandingPage() : const LandingPage(),

@ -39,7 +39,12 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget {
alignment: context.locale.languageCode == "ar" ? Alignment.centerRight : Alignment.centerLeft,
child: GestureDetector(
onTap: onBackPressed,
child: context.locale.languageCode == "ar"
? RotatedBox(
quarterTurns: 90,
child: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 32.h, height: 32.h),
)
: Utils.buildSvgWithAssets(icon: AppAssets.arrow_back, width: 32.h, height: 32.h),
),
),
),

@ -78,7 +78,6 @@ class _GenericBottomSheetState extends State<GenericBottomSheet> {
bottom: Platform.isIOS ? false : true,
child: GestureDetector(
onTap: () {
// Dismiss keyboard and unfocus text field
_textFieldFocusNode.unfocus();
FocusScope.of(context).unfocus();
},
@ -130,7 +129,7 @@ class _GenericBottomSheetState extends State<GenericBottomSheet> {
else ...[
widget.textController != null
? TextInputWidget(
labelText: widget.isForEmail ? LocaleKeys.email : LocaleKeys.phoneNumber,
labelText: widget.isForEmail ? LocaleKeys.email.tr() : LocaleKeys.phoneNumber.tr(),
hintText: widget.isForEmail ? "demo@gmail.com" : "5xxxxxxxx",
controller: widget.textController!,
focusNode: _textFieldFocusNode,

@ -41,7 +41,6 @@ class AppCustomChipWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("detected icon: $deleteIcon");
return ChipTheme(
data: ChipThemeData(
padding: EdgeInsets.all(0.0),

@ -14,7 +14,6 @@ class CustomCountryDropdown extends StatefulWidget {
final List<CountryEnum> countryList;
final Function(CountryEnum)? onCountryChange;
final Function(String)? onPhoneNumberChanged;
final bool isRtl;
final bool isFromBottomSheet;
final bool isEnableTextField;
Widget? textField;
@ -24,7 +23,6 @@ class CustomCountryDropdown extends StatefulWidget {
required this.countryList,
this.onCountryChange,
this.onPhoneNumberChanged,
required this.isRtl,
this.isFromBottomSheet = false,
this.isEnableTextField = false,
this.textField,
@ -147,11 +145,23 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
void _openDropdown() {
if (textFocusNode.hasFocus) {
textFocusNode.unfocus();
// Wait for keyboard to close before calculating position
Future.delayed(Duration(milliseconds: 300), () {
_showDropdown();
});
} else {
_showDropdown();
}
}
void _showDropdown() {
AppState appState = getIt.get<AppState>();
RenderBox renderBox = context.findRenderObject() as RenderBox;
Offset offset = renderBox.localToGlobal(Offset.zero);
bool isRtl = appState.getLanguageCode() == "ar";
double leftPosition = isRtl ? offset.dx + 8 + renderBox.size.width - (!widget.isFromBottomSheet ? renderBox.size.width : 60.h) : offset.dx;
_overlayEntry = OverlayEntry(
builder: (context) => Stack(
children: [
@ -164,7 +174,7 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
),
Positioned(
top: offset.dy + renderBox.size.height,
left: widget.isRtl ? offset.dx + 6.h : offset.dx - 6.h,
left: leftPosition,
width: !widget.isFromBottomSheet ? renderBox.size.width : 60.h,
child: Material(
child: Container(
@ -209,6 +219,71 @@ class _CustomCountryDropdownState extends State<CustomCountryDropdown> {
});
}
// void _openDropdown() {
// if (textFocusNode.hasFocus) {
// textFocusNode.unfocus();
// }
// AppState appState = getIt.get<AppState>();
// RenderBox renderBox = context.findRenderObject() as RenderBox;
// Offset offset = renderBox.localToGlobal(Offset.zero);
//
// _overlayEntry = OverlayEntry(
// builder: (context) => Stack(
// children: [
// Positioned.fill(
// child: GestureDetector(
// onTap: _closeDropdown,
// behavior: HitTestBehavior.translucent,
// child: Container(),
// ),
// ),
// Positioned(
// top: offset.dy + renderBox.size.height,
// left: offset.dx,
// width: !widget.isFromBottomSheet ? renderBox.size.width : 60.h,
// child: Material(
// child: Container(
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: Colors.white, borderRadius: 12),
// child: Column(
// children: widget.countryList
// .map(
// (country) => GestureDetector(
// onTap: () {
// setState(() {
// selectedCountry = country;
// });
// widget.onCountryChange?.call(country);
// _closeDropdown();
// },
// child: Container(
// padding: EdgeInsets.symmetric(vertical: 8.h, horizontal: 8.h),
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(borderRadius: 16.h),
// child: Row(
// children: [
// Utils.buildSvgWithAssets(icon: country.iconPath, width: 38.h, height: 38.h),
// if (!widget.isFromBottomSheet) SizedBox(width: 12.h),
// if (!widget.isFromBottomSheet)
// Text(appState.getLanguageCode() == "ar" ? country.nameArabic : country.displayName,
// style: TextStyle(fontSize: 14.fSize, height: 21 / 14, fontWeight: FontWeight.w500, letterSpacing: -0.2)),
// ],
// ),
// )),
// )
// .toList(),
// ),
// ),
// ),
// ),
// ],
// ),
// );
//
// Overlay.of(context)?.insert(_overlayEntry);
// setState(() {
// _isDropdownOpen = true;
// });
// }
void _closeDropdown() {
_overlayEntry.remove();
setState(() {

@ -126,7 +126,6 @@ class TextInputWidget extends StatelessWidget {
? CustomCountryDropdown(
countryList: CountryEnum.values,
onCountryChange: onCountryChange,
isRtl: Directionality.of(context) == TextDirection.rtl,
isFromBottomSheet: isCountryDropDown,
isEnableTextField: true,
onPhoneNumberChanged: onChange,
@ -191,6 +190,7 @@ class TextInputWidget extends StatelessWidget {
switcherIcon: Utils.buildSvgWithAssets(icon: AppAssets.language, width: 24.h, height: 24.h),
language: appState.getLanguageCode()!,
initialDate: DateTime.now(),
fontFamily: appState.getLanguageCode() == "ar" ? "GESSTwo" : "Poppins",
okWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.confirm, width: 24.h, height: 24.h)),
cancelWidget: Padding(padding: EdgeInsets.only(right: 8.h), child: Utils.buildSvgWithAssets(icon: AppAssets.cancel, iconColor: Colors.white, width: 24.h, height: 24.h)),
onCalendarTypeChanged: (bool value) {

@ -66,6 +66,7 @@ dependencies:
firebase_analytics: ^11.5.1
jiffy: ^6.4.3
hijri_gregorian_calendar: ^0.1.1
sms_otp_auto_verify: ^2.2.0
web: any
flutter_staggered_animations: ^1.1.1

Loading…
Cancel
Save