login for other country done.

login_other_country
Sultan khan 2 weeks ago
parent 13cf05819e
commit 63e63baf7a

@ -21,8 +21,8 @@ var PACKAGES_ORDERS = '/api/orders';
var PACKAGES_ORDER_HISTORY = '/api/orders/items';
var PACKAGES_TAMARA_OPT = '/api/orders/paymentoptions/tamara';
// var BASE_URL = 'http://10.50.100.198:2018/';
// var BASE_URL = 'https://uat.hmgwebservices.com/';
var BASE_URL = 'https://hmgwebservices.com/';
var BASE_URL = 'https://uat.hmgwebservices.com/';
// var BASE_URL = 'https://hmgwebservices.com/';
// var BASE_URL = 'http://10.201.204.103/';
// var BASE_URL = 'https://orash.cloudsolutions.com.sa/';
// var BASE_URL = 'https://vidauat.cloudsolutions.com.sa/';
@ -357,7 +357,7 @@ var CAN_PAY_FOR_FOR_WALKIN_APPOINTMENT = 'Services/Doctors.svc/REST/CanPayForWal
var CHANNEL = 3;
var GENERAL_ID = 'Cs2020@2016\$2958';
var IP_ADDRESS = '10.20.10.20';
var VERSION_ID = 19.4;
var VERSION_ID = 20.5;
var SETUP_ID = '91877';
var LANGUAGE = 2;
// var PATIENT_OUT_SA = 0;

@ -1,5 +1,6 @@
class CheckActivationCodeReq {
int? patientMobileNumber;
String? patientMobileNumberOthers; // For Others country - string mobile number
String? mobileNo;
String? deviceToken;
bool? projectOutSA;
@ -26,6 +27,7 @@ class CheckActivationCodeReq {
CheckActivationCodeReq({
this.patientMobileNumber,
this.patientMobileNumberOthers,
this.mobileNo,
this.deviceToken,
this.projectOutSA,
@ -53,6 +55,7 @@ class CheckActivationCodeReq {
CheckActivationCodeReq.fromJson(Map<String, dynamic> json) {
patientMobileNumber = json['PatientMobileNumber'];
patientMobileNumberOthers = json['PatientMobileNumber_Others'];
mobileNo = json['MobileNo'];
deviceToken = json['DeviceToken'];
projectOutSA = json['ProjectOutSA'];
@ -81,7 +84,12 @@ class CheckActivationCodeReq {
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['PatientMobileNumber'] = this.patientMobileNumber;
data['MobileNo'] = this.mobileNo;
if (this.patientMobileNumberOthers != null) {
data['PatientMobileNumber_Others'] = this.patientMobileNumberOthers;
// Don't include MobileNo for Others country
} else {
data['MobileNo'] = this.mobileNo;
}
data['DeviceToken'] = this.deviceToken;
data['ProjectOutSA'] = this.projectOutSA;
data['LoginType'] = this.loginType;

@ -1,5 +1,6 @@
class CheckPatientAuthenticationReq {
int? patientMobileNumber;
String? patientMobileNumberOthers;
String? zipCode;
bool? isRegister;
String? tokenID;
@ -20,6 +21,7 @@ class CheckPatientAuthenticationReq {
String? healthId;
CheckPatientAuthenticationReq(
{this.patientMobileNumber,
this.patientMobileNumberOthers,
this.zipCode,
this.isRegister,
this.tokenID,
@ -42,6 +44,7 @@ class CheckPatientAuthenticationReq {
CheckPatientAuthenticationReq.fromJson(Map<String, dynamic> json) {
patientMobileNumber = json['PatientMobileNumber'];
patientMobileNumberOthers = json['PatientMobileNumber_Others'];
zipCode = json['ZipCode'];
isRegister = json['isRegister'];
tokenID = json['TokenID'];
@ -65,6 +68,7 @@ class CheckPatientAuthenticationReq {
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['PatientMobileNumber'] = this.patientMobileNumber;
data['PatientMobileNumber_Others'] = this.patientMobileNumberOthers;
data['ZipCode'] = this.zipCode;
data['isRegister'] = this.isRegister;
data['TokenID'] = this.tokenID;

@ -13,7 +13,7 @@ class SelectDeviceIMEIRES {
int? patientID;
int? patientType;
int? preferredLanguage;
bool? isOther;
SelectDeviceIMEIRES(
{this.biometricEnabled,
this.createdOn,
@ -28,7 +28,9 @@ class SelectDeviceIMEIRES {
this.outSA,
this.patientID,
this.patientType,
this.preferredLanguage});
this.preferredLanguage,
this.isOther
});
SelectDeviceIMEIRES.fromJson(Map<String, dynamic> json) {
biometricEnabled = json['BiometricEnabled'];
@ -45,6 +47,7 @@ class SelectDeviceIMEIRES {
patientID = json['PatientID'];
patientType = json['PatientType'];
preferredLanguage = json['PreferredLanguage'];
isOther =json["IsOther"];
}
Map<String, dynamic> toJson() {
@ -63,6 +66,8 @@ class SelectDeviceIMEIRES {
data['PatientID'] = this.patientID;
data['PatientType'] = this.patientType;
data['PreferredLanguage'] = this.preferredLanguage;
data['IsOther'] = this.isOther;
return data;
}

@ -1,5 +1,6 @@
class SendActivationRequest {
int? patientMobileNumber;
String? patientMobileNumberOthers;
String? mobileNo;
String? deviceToken;
bool? projectOutSA;
@ -31,6 +32,7 @@ class SendActivationRequest {
bool? isPatientExcluded;
SendActivationRequest(
{this.patientMobileNumber,
this.patientMobileNumberOthers,
this.mobileNo,
this.deviceToken,
this.projectOutSA,
@ -64,6 +66,7 @@ class SendActivationRequest {
SendActivationRequest.fromJson(Map<String, dynamic> json) {
patientMobileNumber = json['PatientMobileNumber'];
patientMobileNumberOthers = json['PatientMobileNumber_Others'];
mobileNo = json['MobileNo'];
deviceToken = json['DeviceToken'];
projectOutSA = json['ProjectOutSA'];
@ -98,6 +101,7 @@ class SendActivationRequest {
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['PatientMobileNumber'] = this.patientMobileNumber;
data['PatientMobileNumber_Others'] = this.patientMobileNumberOthers;
data['MobileNo'] = this.mobileNo;
data['DeviceToken'] = this.deviceToken;
data['ProjectOutSA'] = this.projectOutSA;

File diff suppressed because it is too large Load Diff

@ -63,7 +63,8 @@ class _ConfirmLogin extends State<ConfirmLogin> {
bool authenticated = false;
final authService = AuthProvider();
PharmacyModuleViewModel pharmacyModuleViewModel = locator<PharmacyModuleViewModel>();
late int mobileNumber;
int? mobileNumber;
String? mobileNumberOthers;
String errorMsg = '';
SelectDeviceIMEIRES? user;
bool isLoading = false;
@ -426,6 +427,17 @@ class _ConfirmLogin extends State<ConfirmLogin> {
request.dob = "";
request.healthId = "";
request.isHijri = 0;
zipCode =='0' ? await this.authService.sendActivationCodeOthers(request).then((result) {
GifLoaderDialogUtils.hideDialog(context);
if (result != null && result['isSMSSent'] == true) {
this.startSMSService(type);
}
}).catchError((r) {
GifLoaderDialogUtils.hideDialog(context);
AppToast.showErrorToast(message: r.toString());
})
:
await this.authService.sendActivationCode(request).then((result) {
GifLoaderDialogUtils.hideDialog(context);
if (result != null && result['isSMSSent'] == true) {
@ -445,7 +457,7 @@ class _ConfirmLogin extends State<ConfirmLogin> {
new SMSOTP(
context,
type,
this.mobileNumber,
zipCode =='0'? this.mobileNumberOthers : this.mobileNumber,
(value) {
this.checkActivationCode(value: value);
},
@ -532,7 +544,9 @@ class _ConfirmLogin extends State<ConfirmLogin> {
this.registerd_data = await CheckPatientAuthenticationReq.fromJson(await sharedPref.getObject(REGISTER_DATA_FOR_LOGIIN));
}
this.mobileNumber = this.registerd_data != null ? this.registerd_data.patientMobileNumber : int.parse(this.user!.mobile!);
this.mobileNumberOthers = this.registerd_data != null ? this.registerd_data.patientMobileNumberOthers : this.user!.mobile;
this.mobileNumber = this.registerd_data != null ? this.registerd_data.patientMobileNumber : int.parse(this.user!.mobile!);
this.zipCode = this.registerd_data != null
? this.registerd_data.zipCode
: this.user!.outSA == true
@ -563,6 +577,10 @@ class _ConfirmLogin extends State<ConfirmLogin> {
? user!.logInType
: null;
if(this.user !=null && this.user!.isOther!){
this.zipCode ='0';
}
showLoader(false);
//this.cs.sharedService.getStorage(AuthenticationService.LAST_LOGIN);
}
@ -570,13 +588,22 @@ class _ConfirmLogin extends State<ConfirmLogin> {
getCommonRequest({type}) {
var request = SendActivationRequest();
request.patientMobileNumber = this.mobileNumber;
request.mobileNo = '0' + this.mobileNumber.toString();
if(this.zipCode=='0'){
request.patientMobileNumberOthers = this.mobileNumberOthers;
request.mobileNo = this.mobileNumberOthers;
}else {
request.mobileNo = '0' + this.mobileNumber.toString();
}
request.deviceToken = this.deviceToken;
request.projectOutSA = this.patientOutSA == true ? true : false;
request.loginType = this.selectedOption;
request.loginType = type ?? this.selectedOption;
request.oTPSendType = type == 1 ? type : 2; //this.selectedOption == 1 ? 1 : 2;
request.zipCode = this.zipCode;
//added new parameter for other country
request.logInTokenID = this.loginTokenID ?? "";
if (this.registerd_data != null) {
@ -644,8 +671,12 @@ class _ConfirmLogin extends State<ConfirmLogin> {
});
});
} else {
authService
.checkActivationCode(request, value)
// Check if Others country (zipCode == "0") and call appropriate API
final activationCodeCall = (this.zipCode == "0")
? authService.checkActivationCodeOthers(request, value)
: authService.checkActivationCode(request, value);
activationCodeCall
.then((result) async => {
res = result,
if (result is Map)
@ -783,7 +814,7 @@ class _ConfirmLogin extends State<ConfirmLogin> {
}
insertIMEI() {
authService.insertDeviceImei(selectedOption).then((value) => {}).catchError((err) {
authService.insertDeviceImei(selectedOption, isForOthers: this.zipCode == "0").then((value) => {}).catchError((err) {
print(err);
});
}
@ -846,8 +877,9 @@ class _ConfirmLogin extends State<ConfirmLogin> {
});
}
Widget _loginOptionButton(String _title, String _icon, int _flag, int? _loginIndex) {
bool isDisable = (_flag == 3 && !checkIfBiometricAvailable(BiometricType.face) ||
Widget _loginOptionButton(String _title, String _icon, int _flag, int? _loginIndex, {bool isDisabledForOthers = false}) {
bool isDisable = isDisabledForOthers ||
(_flag == 3 && !checkIfBiometricAvailable(BiometricType.face) ||
_flag == 2 && !checkIfBiometricAvailable(BiometricType.fingerprint) && _flag == 2 && !checkIfBiometricAvailable(BiometricType.strong));
return InkWell(
onTap: isDisable
@ -894,18 +926,21 @@ class _ConfirmLogin extends State<ConfirmLogin> {
// todo 'sikander' optimize this after knowing usage
Widget getButton(int flag) {
// Disable SMS for Others country (zipCode == "0")
bool isDisabledForOthers = (flag == 1 && this.zipCode == "0");
switch (flag) {
case 4:
return _loginOptionButton(TranslationBase.of(context).verifyWhatsApp, 'assets/images/new/verify_whatsapp.svg', flag, null);
return _loginOptionButton(TranslationBase.of(context).verifyWhatsApp, 'assets/images/new/verify_whatsapp.svg', flag, null, isDisabledForOthers: false);
break;
case 1:
return _loginOptionButton(TranslationBase.of(context).verifySMS, 'assets/images/new/verify_sms.svg', flag, null);
return _loginOptionButton(TranslationBase.of(context).verifySMS, 'assets/images/new/verify_sms.svg', flag, null, isDisabledForOthers: isDisabledForOthers);
break;
case 2:
return _loginOptionButton(TranslationBase.of(context).verifyFingerprint, 'assets/images/new/verify_thumb.svg', flag, BiometricType.fingerprint.index);
return _loginOptionButton(TranslationBase.of(context).verifyFingerprint, 'assets/images/new/verify_thumb.svg', flag, BiometricType.fingerprint.index, isDisabledForOthers: false);
break;
case 3:
return _loginOptionButton(TranslationBase.of(context).verifyFaceID, 'assets/images/new/verify_face.svg', flag, BiometricType.face.index);
return _loginOptionButton(TranslationBase.of(context).verifyFaceID, 'assets/images/new/verify_face.svg', flag, BiometricType.face.index, isDisabledForOthers: false);
break;
default:

@ -198,7 +198,7 @@ class _Login extends State<Login> {
TextField(
enabled: isEnable,
scrollPadding: EdgeInsets.zero,
keyboardType: TextInputType.number,
keyboardType: countryCode =='0'? TextInputType.text: TextInputType.number,
controller: _controller,
onChanged: (value) => {validateForm()},
style: TextStyle(
@ -254,8 +254,13 @@ class _Login extends State<Login> {
}
void validateForm() {
if (util.validateIDBox(nationalIDorFile.text, loginType) == true &&
(countryCode == "971" ? true : util.isSAUDIIDValid(nationalIDorFile.text, loginType) == true)) {
// For "Others" country, skip Saudi ID validation
bool idValidation = util.validateIDBox(nationalIDorFile.text, loginType) == true || countryCode == "0";
bool countrySpecificValidation = (countryCode == "971" || countryCode == "0")
? true
: util.isSAUDIIDValid(nationalIDorFile.text, loginType) == true;
if (idValidation && countrySpecificValidation && mobileNo != null && mobileNo!.isNotEmpty) {
setState(() {
isButtonDisabled = false;
});
@ -270,7 +275,18 @@ class _Login extends State<Login> {
// showLoader(true);
var request = CheckPatientAuthenticationReq();
request.isRegister = false;
request.patientMobileNumber = int.parse(mobileNo!);
// For Others country (countryCode == "0"), pass mobile number as STRING
// For KSA/UAE, pass mobile number as INT
if (countryCode == "0") {
// Mobile number as string for international format (e.g., "+1234567890")
request.patientMobileNumberOthers = mobileNo; // String
} else {
// Mobile number as integer for KSA/UAE (e.g., 501234567)
request.patientMobileNumber = int.parse(mobileNo!); // Int
}
request.zipCode = countryCode;
request.searchType = this.loginType;
request.deviceTypeID = this.loginType;
@ -283,7 +299,14 @@ class _Login extends State<Login> {
}
sharedPref.setObject(REGISTER_DATA_FOR_REGISTER, request);
GifLoaderDialogUtils.showMyDialog(context);
authService.checkPatientAuthentication(request).then((value) {
// Use checkPatientAuthenticationOthers for Others country (countryCode == "0")
// Use checkPatientAuthentication for KSA/UAE (countryCode == "966" or "971")
final authCall = (countryCode == "0")
? authService.checkPatientAuthenticationOthers(request)
: authService.checkPatientAuthentication(request);
authCall.then((value) {
// if (value['UserAccountIsActivated']) {
if (value['isSMSSent']) {
sharedPref.setString(LOGIN_TOKEN_ID, value['LogInTokenID']);

@ -34,13 +34,17 @@ AppSharedPreferences sharedPref = AppSharedPreferences();
AppGlobal appGlobal = AppGlobal();
const String INSERT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_INSERTDeviceIMEI';
const INSERT_DEVICE_IMEI_OTHERS ='Services/Patients.svc/REST/Patient_INSERTDeviceIMEI_Others';
const String SELECT_DEVICE_IMEI = 'Services/Patients.svc/REST/Patient_SELECTDeviceIMEIbyIMEI';
const String CHECK_PATIENT_AUTH = 'Services/Authentication.svc/REST/CheckPatientAuthentication';
const String CHECK_PATIENT_AUTH_OTHERS = 'Services/Authentication.svc/REST/CheckPatientAuthentication_Others';
const GET_MOBILE_INFO = 'Services/Authentication.svc/REST/GetMobileLoginInfo';
const SEND_ACTIVATION_CODE = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationType';
const SEND_ACTIVATION_CODE_OTHERS = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationType_Others';
const SEND_ACTIVATION_CODE_REGISTER = 'Services/Authentication.svc/REST/SendActivationCodebyOTPNotificationTypeForRegistration';
const CHECK_ACTIVATION_CODE = 'Services/Authentication.svc/REST/CheckActivationCode';
const CHECK_ACTIVATION_CODE_OTHERS = 'Services/Authentication.svc/REST/CheckActivationCode_Others';
const CHECK_ACTIVATION_CODE_REGISTER = 'Services/Authentication.svc/REST/CheckActivationCodeForRegistration';
const FORGOT_PASSWORD = 'Services/Authentication.svc/REST/CheckActivationCodeForSendFileNo';
@ -111,7 +115,7 @@ class AuthProvider with ChangeNotifier {
// }
// }
Future<dynamic> insertDeviceImei(lstLogin) async {
Future<dynamic> insertDeviceImei(lstLogin,{ bool isForOthers = false}) async {
try {
dynamic localRes;
var lastLogin = lstLogin; //await sharedPref.getInt(
@ -137,9 +141,8 @@ class AuthProvider with ChangeNotifier {
newRequest.patientType = request.patientType;
newRequest.setupID = request.setupID;
newRequest.tokenID = await sharedPref.getString(TOKEN);
// }
await new BaseAppClient().post(INSERT_DEVICE_IMEI, onSuccess: (dynamic response, int statusCode) {
await new BaseAppClient().post(isForOthers ? INSERT_DEVICE_IMEI_OTHERS : INSERT_DEVICE_IMEI, onSuccess: (dynamic response, int statusCode) {
localRes = response;
}, onFailure: (String error, int statusCode) {
throw error;
@ -196,7 +199,7 @@ class AuthProvider with ChangeNotifier {
request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1;
try {
dynamic localRes;
await new BaseAppClient().post(CHECK_PATIENT_AUTH, onSuccess: (dynamic response, int statusCode) {
await new BaseAppClient().post( request.zipCode =='0' ? CHECK_PATIENT_AUTH_OTHERS : CHECK_PATIENT_AUTH, onSuccess: (dynamic response, int statusCode) {
localRes = response;
}, onFailure: (String error, int statusCode) {
throw error;
@ -208,6 +211,27 @@ class AuthProvider with ChangeNotifier {
}
}
// New function for Others country authentication
Future<dynamic> checkPatientAuthenticationOthers(CheckPatientAuthenticationReq request) async {
request.versionID = VERSION_ID;
request.channel = CHANNEL;
request.iPAdress = IP_ADDRESS;
request.generalid = GENERAL_ID;
request.languageID = (languageID == 'ar' ? 1 : 2);
request.patientOutSA = 1; // Always set to 1 for Others (outside KSA/UAE)
try {
dynamic localRes;
await new BaseAppClient().post(CHECK_PATIENT_AUTH_OTHERS, onSuccess: (dynamic response, int statusCode) {
localRes = response;
}, onFailure: (String error, int statusCode) {
throw error;
}, body: request.toJson());
return Future.value(localRes);
} catch (error) {
throw error;
}
}
Future<dynamic> getLoginInfo(request) async {
request.versionID = VERSION_ID;
request.channel = CHANNEL;
@ -249,6 +273,28 @@ class AuthProvider with ChangeNotifier {
return Future.value(localRes);
}
// New method for Others country activation code
Future<dynamic> sendActivationCodeOthers(request) async {
request.versionID = VERSION_ID;
request.channel = CHANNEL;
request.iPAdress = IP_ADDRESS;
request.generalid = GENERAL_ID;
// request.languageID = (languageID == 'ar' ? 1 : 2);
request.languageID = 2;
request.deviceTypeID = Platform.isIOS ? 1 : 2;
request.patientOutSA = 1; // Always 1 for Others (outside KSA/UAE)
request.isDentalAllowedBackend = false;
dynamic localRes;
await new BaseAppClient().post(SEND_ACTIVATION_CODE_OTHERS, onSuccess: (dynamic response, int statusCode) {
localRes = response;
authenticatedUser = CheckActivationCode.fromJson(localRes);
}, onFailure: (String error, int statusCode) {
throw error;
}, body: request.toJson());
return Future.value(localRes);
}
Future<dynamic> sendActivationCodeRegister(request) async {
request.versionID = VERSION_ID;
request.channel = CHANNEL;
@ -282,6 +328,8 @@ class AuthProvider with ChangeNotifier {
neRequest.patientOutSA = neRequest.zipCode == '966' ? 0 : 1;
neRequest.projectOutSA = neRequest.zipCode == '966' ? false : true;
neRequest.isDentalAllowedBackend = false;
// neRequest.zipCode ='966'; //hardcoded for testing
// request.languageID = (languageID == 'ar' ? 1 : 2)
// neRequest.deviceToken = null;
neRequest.forRegisteration = neRequest.isRegister != null ? neRequest.isRegister : false;
neRequest.isRegister = false;
@ -300,6 +348,35 @@ class AuthProvider with ChangeNotifier {
}
}
Future<dynamic> checkActivationCodeOthers(request, [value]) async {
var neRequest = CheckActivationCodeReq.fromJson(request);
neRequest.activationCode = value ?? "0000";
neRequest.isSilentLogin = value != null ? false : true;
neRequest.versionID = VERSION_ID;
neRequest.channel = CHANNEL;
neRequest.iPAdress = IP_ADDRESS;
neRequest.generalid = GENERAL_ID;
neRequest.deviceTypeID = Platform.isIOS ? 1 : 2;
neRequest.patientOutSA = 1; // Always 1 for Others (outside KSA/UAE)
neRequest.projectOutSA = true; // Always true for Others
neRequest.isDentalAllowedBackend = false;
neRequest.forRegisteration = neRequest.isRegister != null ? neRequest.isRegister : false;
neRequest.isRegister = false;
dynamic localRes;
try {
await new BaseAppClient().post(CHECK_ACTIVATION_CODE_OTHERS, onSuccess: (dynamic response, int statusCode) {
localRes = response;
}, onFailure: (String error, int statusCode) {
localRes = error;
return Future.value(error);
}, body: neRequest.toJson());
return Future.value(localRes);
} catch (error) {
throw localRes;
}
}
Future<dynamic> checkActivationCodeRegister(request, [value]) async {
var neRequest = CheckActivationCodeRegisterReq.fromJson(request);
@ -574,8 +651,7 @@ class AuthProvider with ChangeNotifier {
// {
// "ID": 12,
// "ServiceName": "Precriptions",
// "Previlege": true,
// "Region": null
// "Previlege": true,// "Region": null
// },
// {
// "ID": 13,

@ -30,6 +30,11 @@ class _PhoneNumberSelectorWidgetState extends State<PhoneNumberSelectorWidget> {
List<Countries> counties = [];
ProjectViewModel? projectProvider;
TextEditingController textController = new TextEditingController();
String? validationError;
// Regular expression to detect KSA (+966) or UAE (+971) phone numbers
// Matches: +966, 966, 00966, +971, 971, 00971 at the start
final RegExp ksaUaePattern = RegExp(r'^(\+|00)?(966|971)');
@override
void initState() {
@ -38,6 +43,19 @@ class _PhoneNumberSelectorWidgetState extends State<PhoneNumberSelectorWidget> {
super.initState();
}
// Validate phone number when "Others" is selected
String? validatePhoneNumber(String value) {
if (countryCode == "0" && value.isNotEmpty) {
// Check if the number starts with KSA or UAE country code
if (ksaUaePattern.hasMatch(value)) {
return projectProvider?.isArabic == true
? "يرجى عدم إدخال أرقام السعودية أو الإمارات. استخدم الخيار المناسب من القائمة"
: "Please do not enter KSA or UAE numbers. Use the appropriate option from the list";
}
}
return null;
}
@override
void dispose() {
super.dispose();
@ -56,16 +74,43 @@ class _PhoneNumberSelectorWidgetState extends State<PhoneNumberSelectorWidget> {
}
}
// Check if "Others" is selected (code == "0")
bool isOthersSelected = countryCode == "0";
return Column(
children: [
inputWidget(TranslationBase.of(context).country, countryName!, isEnable: true, hasSelection: true),
SizedBox(height: 12),
Directionality(textDirection: TextDirection.ltr, child: inputWidget(TranslationBase.of(context).phoneNumber, isEnable: widget.isEnable, widget.mobileNo ?? "5xxxxxxxx", prefix: countryCode)),
Directionality(
textDirection: TextDirection.ltr,
child: inputWidget(
TranslationBase.of(context).phoneNumber,
isEnable: widget.isEnable,
widget.mobileNo ?? (isOthersSelected ? "" : "5xxxxxxxx"),
prefix: isOthersSelected ? null : countryCode
)
),
if (validationError != null)
Container(
width: double.infinity,
margin: EdgeInsets.only(top: 8),
child: Text(
validationError!,
style: TextStyle(
color: Colors.red,
fontSize: 12,
fontWeight: FontWeight.w500,
),
),
),
],
);
}
Widget inputWidget(String _labelText, String _hintText, {String? prefix, bool isEnable = true, bool hasSelection = false}) {
// Check if "Others" is selected
bool isOthersSelected = countryCode == "0";
return Container(
padding: EdgeInsets.only(left: 16, right: 16, bottom: 15, top: 15),
alignment: Alignment.center,
@ -73,8 +118,8 @@ class _PhoneNumberSelectorWidgetState extends State<PhoneNumberSelectorWidget> {
borderRadius: BorderRadius.circular(15),
color: Colors.white,
border: Border.all(
color: Color(0xffefefef),
width: 1,
color: validationError != null ? Colors.red : Color(0xffefefef),
width: validationError != null ? 1.5 : 1,
),
),
child: PopupMenuButton(
@ -99,8 +144,20 @@ class _PhoneNumberSelectorWidgetState extends State<PhoneNumberSelectorWidget> {
controller: hasSelection ? null : textController,
enabled: isEnable,
scrollPadding: EdgeInsets.zero,
keyboardType: TextInputType.number,
onChanged: (value) => widget.onNumberChange!(value),
keyboardType: isOthersSelected ? TextInputType.phone : TextInputType.number,
onChanged: (value) {
// Validate the phone number
setState(() {
validationError = validatePhoneNumber(value);
});
// Only call the callback if validation passes
if (validationError == null) {
widget.onNumberChange!(value);
} else {
// Call with empty value to invalidate the form
widget.onNumberChange!("");
}
},
style: TextStyle(
fontSize: 14,
height: 21 / 14,
@ -135,6 +192,8 @@ class _PhoneNumberSelectorWidgetState extends State<PhoneNumberSelectorWidget> {
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
errorBorder: InputBorder.none,
focusedErrorBorder: InputBorder.none,
),
),
],
@ -151,6 +210,11 @@ class _PhoneNumberSelectorWidgetState extends State<PhoneNumberSelectorWidget> {
setState(() {
this.countryCode = countryCode;
_selectedType = countryCode;
// Clear the text field and validation error when switching to/from "Others"
if (countryCode == "0" || this.countryCode == "0") {
textController.clear();
validationError = null;
}
});
widget.onCountryChange!(countryCode);
},

@ -265,15 +265,26 @@ class SMSOTP {
startTimer(setState) {
this.remainingTime--;
setState(() {
displayTime = this.getSecondsAsDigitalClock(this.remainingTime);
});
// Check if the widget is still mounted before calling setState
try {
setState(() {
displayTime = this.getSecondsAsDigitalClock(this.remainingTime);
});
} catch (e) {
// Widget is no longer mounted, stop the timer
return;
}
timer = Future.delayed(Duration(seconds: 1), () {
if (this.remainingTime > 0) {
startTimer(setState);
} else {
Navigator.pop(context);
// Check if context is still valid before trying to pop
try {
Navigator.pop(context);
} catch (e) {
// Context is no longer valid, ignore
}
}
});
}

Loading…
Cancel
Save