e-referral work done search-referral in progress.

pull/104/head
Sultan khan 2 months ago
parent 9a04974db3
commit d8a9d8443a

@ -178,8 +178,12 @@ var DELETE_CHILD_REQUEST = 'Services/Community.svc/REST/DeleteBaby';
var GET_TABLE_REQUEST = 'Services/Community.svc/REST/CreateVaccinationTable';
///BloodDenote
///
/// use get all cities from the e-referral, already calling this api there don't use multiple same api calls
var GET_CITIES_REQUEST = 'Services/Lists.svc/REST/GetAllCities';
///
///
///
///BloodDetails
var GET_BLOOD_REQUEST = 'services/PatientVarification.svc/REST/BloodDonation_GetBloodGroupDetails';
@ -440,12 +444,7 @@ var H2O_UPDATE_USER_DETAIL = "Services/H2ORemainder.svc/REST/H2O_UpdateUserDetai
var H2O_UNDO_USER_ACTIVITY = "Services/H2ORemainder.svc/REST/H2o_UndoUserActivity";
//E_Referral Services
var GET_ALL_RELATIONSHIP_TYPES = "Services/Patients.svc/REST/GetAllRelationshipTypes";
var SEND_ACTIVATION_CODE_FOR_E_REFERRAL = 'Services/Authentication.svc/REST/SendActivationCodeForEReferral';
var CHECK_ACTIVATION_CODE_FOR_E_REFERRAL = 'Services/Authentication.svc/REST/CheckActivationCodeForEReferral';
var GET_ALL_CITIES = 'services/Lists.svc/rest/GetAllCities';
var CREATE_E_REFERRAL = "Services/Patients.svc/REST/CreateEReferral";
var GET_E_REFERRALS = "Services/Patients.svc/REST/GetEReferrals";
// Encillary Orders
@ -849,6 +848,17 @@ class ApiConsts {
var GET_ALL_CMC_ORDERS_RC = 'api/cmc/list';
var UPDATE_CMC_ORDER_RC = 'api/cmc/update';
//E-REFERRAL SERVICES
static final getAllRelationshipTypes = "Services/Patients.svc/REST/GetAllRelationshipTypes";
static final sendActivationCodeForEReferral = 'Services/Authentication.svc/REST/SendActivationCodeForEReferral';
static final checkActivationCodeForEReferral = 'Services/Authentication.svc/REST/CheckActivationCodeForEReferral';
static final getAllCities = 'services/Lists.svc/rest/GetAllCities';
static final createEReferral = "Services/Patients.svc/REST/CreateEReferral";
static final getEReferrals = "Services/Patients.svc/REST/GetEReferrals";
// ************ static values for Api ****************
static final double appVersionID = 20.0;
static final int appChannelId = 3;

@ -101,8 +101,7 @@ class ValidationUtils {
return regex.hasMatch(id);
}
static bool validateUaeRegistration(
{String? name, GenderTypeEnum? gender, NationalityCountries? country, MaritalStatusTypeEnum? maritalStatus, required Function() onOkPress}) {
static bool validateUaeRegistration({String? name, GenderTypeEnum? gender, NationalityCountries? country, MaritalStatusTypeEnum? maritalStatus, required Function() onOkPress}) {
if (name == null || name.isEmpty) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAValidName.tr(), onOkPressed: onOkPress);
return false;
@ -141,8 +140,7 @@ class ValidationUtils {
return true;
}
static bool isValidatedIdAndPhoneWithCountryValidation(
{String? nationalId, String? phoneNumber, required Function() onOkPress, CountryEnum? selectedCountry}) {
static bool isValidatedIdAndPhoneWithCountryValidation({String? nationalId, String? phoneNumber, required Function() onOkPress, CountryEnum? selectedCountry}) {
bool isCorrectID = true;
if (nationalId == null || nationalId.isEmpty) {
_dialogService.showExceptionBottomSheet(message: LocaleKeys.pleaseEnterAnationalID.tr(), onOkPressed: onOkPress);
@ -171,4 +169,10 @@ class ValidationUtils {
}
return isCorrectID;
}
}
static bool isNullOrEmpty(String? value) {
return value == null || value
.trim()
.isEmpty;
}
}

@ -5,10 +5,17 @@ import 'package:hmg_patient_app_new/core/api/api_client.dart';
import 'package:hmg_patient_app_new/core/api_consts.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/check_activation_e_referral_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:provider/provider.dart';
import 'models/req_models/create_e_referral_model.dart';
import 'models/req_models/send_activation_code_ereferral_req_model.dart';
import 'models/resq_models/relationship_type_resp_mode.dart';
abstract class HmgServicesRepo {
Future<Either<Failure, GenericApiModel<List<GetCMCAllOrdersResponseModel>>>> getAllCmcOrders();
@ -16,6 +23,17 @@ abstract class HmgServicesRepo {
Future<Either<Failure, GenericApiModel<bool>>> updateCmcPresOrder(OrderUpdateRequestModel requestModel);
Future<Either<Failure, GenericApiModel<List<GetCMCServicesResponseModel>>>> getAllCmcServices({required int patientID});
Future<Either<Failure, GenericApiModel<List<GetAllRelationshipTypeResponseModel>>>> getRelationshipTypes();
Future<Either<Failure, GenericApiModel<List<GetAllCitiesResponseModel>>>> getAllCities();
Future<Either<Failure, GenericApiModel<String>>> sendEReferralActivationCode(SendActivationCodeForEReferralRequestModel requestModel);
Future<Either<Failure, GenericApiModel<String>>> checkEReferralActivationCode(CheckActivationCodeForEReferralRequestModel requestModel);
Future<Either<Failure, GenericApiModel<String>>> createEReferral(CreateEReferralRequestModel requestModel);
}
class HmgServicesRepoImp implements HmgServicesRepo {
@ -175,4 +193,224 @@ class HmgServicesRepoImp implements HmgServicesRepo {
return Left(UnknownFailure(e.toString()));
}
}
/*e-referral functions*/
@override
Future<Either<Failure, GenericApiModel<List<GetAllRelationshipTypeResponseModel>>>> getRelationshipTypes() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<GetAllRelationshipTypeResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getAllRelationshipTypes,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<GetAllRelationshipTypeResponseModel> relationshipList = [];
if (response['List_EReferralResult'] != null && response['List_EReferralResult'] is List) {
final servicesList = response['List_EReferralResult'] as List;
for (var serviceJson in servicesList) {
if (serviceJson is Map<String, dynamic>) {
relationshipList.add(GetAllRelationshipTypeResponseModel.fromJson(serviceJson));
}
}
}
apiResponse = GenericApiModel<List<GetAllRelationshipTypeResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: relationshipList,
);
} catch (e) {
loggerService.logError("Error parsing E-Referral services: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
log("Unknown error in getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<List<GetAllCitiesResponseModel>>>> getAllCities() async {
Map<String, dynamic> requestBody = {};
try {
GenericApiModel<List<GetAllCitiesResponseModel>>? apiResponse;
Failure? failure;
await apiClient.post(
ApiConsts.getAllCities,
body: requestBody,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
List<GetAllCitiesResponseModel> citiesList = [];
if (response['ListCities'] != null && response['ListCities'] is List) {
final servicesList = response['ListCities'] as List;
for (var serviceJson in servicesList) {
if (serviceJson is Map<String, dynamic>) {
citiesList.add(GetAllCitiesResponseModel.fromJson(serviceJson));
}
}
}
apiResponse = GenericApiModel<List<GetAllCitiesResponseModel>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: citiesList,
);
} catch (e) {
loggerService.logError("Error parsing E-Referral services: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
log("Unknown error in getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<String>>> sendEReferralActivationCode(SendActivationCodeForEReferralRequestModel requestModel) async {
GenericApiModel<String>? apiResponse;
try {
Failure? failure;
await apiClient.post(
ApiConsts.sendActivationCodeForEReferral,
body: requestModel.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<String>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response['LogInTokenID'] as String,
);
} catch (e) {
loggerService.logError("Error parsing E-Referral services: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
log("Unknown error in getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<String>>> createEReferral(CreateEReferralRequestModel requestModel) async {
GenericApiModel<String>? apiResponse;
try {
Failure? failure;
await apiClient.post(
ApiConsts.createEReferral,
body: requestModel.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<String>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response['ReferralNumber'].toString(),
);
} catch (e) {
loggerService.logError("Error parsing E-Referral services: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
log("Unknown error in getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
@override
Future<Either<Failure, GenericApiModel<String>>> checkEReferralActivationCode(CheckActivationCodeForEReferralRequestModel requestModel) async {
GenericApiModel<String>? apiResponse;
try {
Failure? failure;
await apiClient.post(
ApiConsts.checkActivationCodeForEReferral,
body: requestModel.toJson(),
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
loggerService.logError("EReferral Services API Failed: $error, Status: $statusCode");
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
apiResponse = GenericApiModel<String>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: errorMessage,
data: response['IsAuthenticated'].toString(),
);
} catch (e) {
loggerService.logError("Error parsing E-Referral services: ${e.toString()}");
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
log("Unknown error in getAllCmcServices: ${e.toString()}");
return Left(UnknownFailure(e.toString()));
}
}
}

@ -1,10 +1,17 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_repo.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/order_update_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_all_orders_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart';
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
import 'models/req_models/check_activation_e_referral_req_model.dart';
import 'models/resq_models/relationship_type_resp_mode.dart';
class HmgServicesViewModel extends ChangeNotifier {
final HmgServicesRepo hmgServicesRepo;
final ErrorHandlerService errorHandlerService;
@ -18,6 +25,10 @@ class HmgServicesViewModel extends ChangeNotifier {
List<GetCMCAllOrdersResponseModel> cmcOrdersList = [];
List<GetCMCServicesResponseModel> cmcServicesList = [];
List<GetAllRelationshipTypeResponseModel> relationTypes =[];
List<GetAllCitiesResponseModel> getAllCitiesList =[];
Future<void> getOrdersList() async {
cmcOrdersList.clear();
isCmcOrdersLoading = true;
@ -134,4 +145,182 @@ class HmgServicesViewModel extends ChangeNotifier {
return success;
}
/*e-referral functions*/
Future<void> getRelationshipType({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.getRelationshipTypes();
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
relationTypes = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> getAllCities({
Function(dynamic)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.getAllCities();
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
getAllCitiesList = apiResponse.data ?? [];
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> eReferralSendActivationCode({
required SendActivationCodeForEReferralRequestModel requestModel,
Function(GenericApiModel)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.sendEReferralActivationCode(requestModel);
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> checkEReferralActivationCode({
required CheckActivationCodeForEReferralRequestModel requestModel,
Function(GenericApiModel)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.checkEReferralActivationCode(requestModel);
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
Future<void> createEReferral({
required CreateEReferralRequestModel requestModel,
Function(GenericApiModel)? onSuccess,
Function(String)? onError,
}) async {
notifyListeners();
final result = await hmgServicesRepo.createEReferral(requestModel);
result.fold(
(failure) async {
notifyListeners();
await errorHandlerService.handleError(failure: failure);
if (onError != null) {
onError(failure.toString());
}
},
(apiResponse) {
if (apiResponse.messageStatus == 1) {
notifyListeners();
if (onSuccess != null) {
onSuccess(apiResponse);
}
} else {
notifyListeners();
if (onError != null) {
onError(apiResponse.errorMessage ?? 'Unknown error');
}
}
},
);
}
}

@ -0,0 +1,57 @@
class CheckActivationCodeForEReferralRequestModel {
String? logInTokenID;
String? activationCode;
double? versionID;
int? channel;
int? languageID;
String? iPAdress;
String? generalid;
int? patientOutSA;
dynamic sessionID;
bool? isDentalAllowedBackend;
int? deviceTypeID;
CheckActivationCodeForEReferralRequestModel(
{this.logInTokenID,
this.activationCode,
this.versionID,
this.channel,
this.languageID,
this.iPAdress,
this.generalid,
this.patientOutSA,
this.sessionID,
this.isDentalAllowedBackend,
this.deviceTypeID});
CheckActivationCodeForEReferralRequestModel.fromJson(
Map<String, dynamic> json) {
logInTokenID = json['LogInTokenID'];
activationCode = json['activationCode'];
versionID = json['VersionID'];
channel = json['Channel'];
languageID = json['LanguageID'];
iPAdress = json['IPAdress'];
generalid = json['generalid'];
patientOutSA = json['PatientOutSA'];
sessionID = json['SessionID'];
isDentalAllowedBackend = json['isDentalAllowedBackend'];
deviceTypeID = json['DeviceTypeID'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['LogInTokenID'] = this.logInTokenID;
data['activationCode'] = this.activationCode;
data['VersionID'] = this.versionID;
data['Channel'] = this.channel;
data['LanguageID'] = this.languageID;
data['IPAdress'] = this.iPAdress;
data['generalid'] = this.generalid;
data['PatientOutSA'] = this.patientOutSA;
data['SessionID'] = this.sessionID;
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
data['DeviceTypeID'] = this.deviceTypeID;
return data;
}
}

@ -0,0 +1,155 @@
class CreateEReferralRequestModel {
bool? isInsuredPatient;
String? cityCode;
String? cityName;
String? requesterName;
String? requesterContactNo;
int? requesterRelationship;
String? otherRelationship;
String? fullName;
int? identificationNo;
String? patientMobileNumber;
int? preferredBranchCode;
String? preferredBranchName;
List<EReferralAttachment>? medicalReportAttachment;
dynamic insuranceCardAttachment;
double? versionID;
int? channel;
int? languageID;
String? iPAdress;
String? generalid;
int? patientOutSA;
String? sessionID;
bool? isDentalAllowedBackend;
int? deviceTypeID;
int? patientID;
String? tokenID;
int? patientTypeID;
int? patientType;
CreateEReferralRequestModel(
{this.isInsuredPatient,
this.cityCode,
this.cityName,
this.requesterName,
this.requesterContactNo,
this.requesterRelationship,
this.otherRelationship,
this.fullName,
this.identificationNo,
this.patientMobileNumber,
this.preferredBranchCode,
this.preferredBranchName,
this.medicalReportAttachment,
this.insuranceCardAttachment,
this.versionID,
this.channel,
this.languageID,
this.iPAdress,
this.generalid,
this.patientOutSA,
this.sessionID,
this.isDentalAllowedBackend,
this.deviceTypeID,
this.patientID,
this.tokenID,
this.patientTypeID,
this.patientType});
CreateEReferralRequestModel.fromJson(Map<String, dynamic> json) {
isInsuredPatient = json['IsInsuredPatient'];
cityCode = json['CityCode'];
cityName = json['CityName'];
requesterName = json['RequesterName'];
requesterContactNo = json['RequesterContactNo'];
requesterRelationship = json['RequesterRelationship'];
otherRelationship = json['OtherRelationship'];
fullName = json['FullName'];
identificationNo = json['IdentificationNo'];
patientMobileNumber = json['PatientMobileNumber'];
preferredBranchCode = json['PreferredBranchCode'];
preferredBranchName = json['PreferredBranchName'];
if (json['MedicalReportAttachment'] != null) {
medicalReportAttachment = <EReferralAttachment>[];
json['MedicalReportAttachment'].forEach((v) {
medicalReportAttachment!.add(EReferralAttachment.fromJson(v));
});
}
insuranceCardAttachment = json['InsuranceCardAttachment'] != null ? EReferralAttachment.fromJson(json['InsuranceCardAttachment']) : null;
versionID = json['VersionID'];
channel = json['Channel'];
languageID = json['LanguageID'];
iPAdress = json['IPAdress'];
generalid = json['generalid'];
patientOutSA = json['PatientOutSA'];
sessionID = json['SessionID'];
isDentalAllowedBackend = json['isDentalAllowedBackend'];
deviceTypeID = json['DeviceTypeID'];
patientID = json['PatientID'];
tokenID = json['TokenID'];
patientTypeID = json['PatientTypeID'];
patientType = json['PatientType'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['IsInsuredPatient'] = this.isInsuredPatient;
data['CityCode'] = this.cityCode;
data['CityName'] = this.cityName;
data['RequesterName'] = this.requesterName;
data['RequesterContactNo'] = this.requesterContactNo;
data['RequesterRelationship'] = this.requesterRelationship;
data['OtherRelationship'] = this.otherRelationship;
data['FullName'] = this.fullName;
data['IdentificationNo'] = this.identificationNo;
data['PatientMobileNumber'] = this.patientMobileNumber;
data['PreferredBranchCode'] = this.preferredBranchCode;
data['PreferredBranchName'] = this.preferredBranchName;
if (this.medicalReportAttachment != null) {
// FIXED: Use map() to convert each item to JSON, then convert to list
data['MedicalReportAttachment'] = this.medicalReportAttachment!.map((v) => v.toJson()).toList();
}
if (this.insuranceCardAttachment == null) {
data['InsuranceCardAttachment'] = {};
} else if (this.insuranceCardAttachment is EReferralAttachment) {
// FIXED: Check if it's an EReferralAttachment before calling toJson()
data['InsuranceCardAttachment'] = (this.insuranceCardAttachment as EReferralAttachment).toJson();
} else {
// If it's something else, assign directly
data['InsuranceCardAttachment'] = this.insuranceCardAttachment;
}
data['VersionID'] = this.versionID;
data['Channel'] = this.channel;
data['LanguageID'] = this.languageID;
data['IPAdress'] = this.iPAdress;
data['generalid'] = this.generalid;
data['PatientOutSA'] = this.patientOutSA;
data['SessionID'] = this.sessionID;
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
data['DeviceTypeID'] = this.deviceTypeID;
data['PatientID'] = this.patientID;
data['TokenID'] = this.tokenID;
data['PatientTypeID'] = this.patientTypeID;
data['PatientType'] = this.patientType;
return data;
}
}
class EReferralAttachment {
String? fileName;
String? base64String;
EReferralAttachment({this.fileName, this.base64String});
EReferralAttachment.fromJson(Map<String, dynamic> json) {
fileName = json['FileName'];
base64String = json['Base64String'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['FileName'] = this.fileName;
data['Base64String'] = this.base64String;
return data;
}
}

@ -0,0 +1,56 @@
class SendActivationCodeForEReferralRequestModel {
int? patientMobileNumber;
String? zipCode;
double? versionID;
int? channel;
int? languageID;
String? iPAdress;
String? generalid;
int? patientOutSA;
dynamic sessionID;
bool? isDentalAllowedBackend;
int? deviceTypeID;
SendActivationCodeForEReferralRequestModel(
{this.patientMobileNumber,
this.zipCode,
this.versionID,
this.channel,
this.languageID,
this.iPAdress,
this.generalid,
this.patientOutSA,
this.sessionID,
this.isDentalAllowedBackend,
this.deviceTypeID});
SendActivationCodeForEReferralRequestModel.fromJson(Map<String, dynamic> json) {
patientMobileNumber = json['PatientMobileNumber'];
zipCode = json['ZipCode'];
versionID = json['VersionID'];
channel = json['Channel'];
languageID = json['LanguageID'];
iPAdress = json['IPAdress'];
generalid = json['generalid'];
patientOutSA = json['PatientOutSA'];
sessionID = json['SessionID'];
isDentalAllowedBackend = json['isDentalAllowedBackend'];
deviceTypeID = json['DeviceTypeID'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['PatientMobileNumber'] = this.patientMobileNumber;
data['ZipCode'] = this.zipCode;
data['VersionID'] = this.versionID;
data['Channel'] = this.channel;
data['LanguageID'] = this.languageID;
data['IPAdress'] = this.iPAdress;
data['generalid'] = this.generalid;
data['PatientOutSA'] = this.patientOutSA;
data['SessionID'] = this.sessionID;
data['isDentalAllowedBackend'] = this.isDentalAllowedBackend;
data['DeviceTypeID'] = this.deviceTypeID;
return data;
}
}

@ -0,0 +1,21 @@
class GetAllCitiesResponseModel {
int? iD;
String? description;
String? descriptionN;
GetAllCitiesResponseModel({this.iD, this.description, this.descriptionN});
GetAllCitiesResponseModel.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
description = json['Description'];
descriptionN = json['DescriptionN'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ID'] = this.iD;
data['Description'] = this.description;
data['DescriptionN'] = this.descriptionN;
return data;
}
}

@ -0,0 +1,25 @@
class GetAllRelationshipTypeResponseModel {
int? iD;
String? text;
String? textAr;
String? textEn;
GetAllRelationshipTypeResponseModel(
{this.iD, this.text, this.textAr, this.textEn});
GetAllRelationshipTypeResponseModel.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
text = json['Text'];
textAr = json['Text_Ar'];
textEn = json['Text_En'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ID'] = this.iD;
data['Text'] = this.text;
data['Text_Ar'] = this.textAr;
data['Text_En'] = this.textEn;
return data;
}
}

@ -0,0 +1,42 @@
// models/referral_models.dart
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/relationship_type_resp_mode.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
class ReferralFormData {
String requesterName = '';
String requesterPhone = '';
CountryEnum countryEnum = CountryEnum.saudiArabia;
GetAllRelationshipTypeResponseModel? relationship;
String otherRelationshipName = '';
String patientIdentification = '';
String patientName = '';
String patientPhone = '';
GetAllCitiesResponseModel? patientCity;
List<EReferralAttachment> medicalReportImages = [];
HospitalsModel? branch;
bool isPatientInsured = false;
List<EReferralAttachment> insuredPatientImages = [];
}
class FormValidationErrors {
String? requesterName;
String? requesterPhone;
String? relationship;
String? otherRelationshipName;
String? patientIdentification;
String? patientName;
String? patientCity;
String? patientPhone;
String? medicalReport;
String? branch;
String? insuredDocument;
}

@ -0,0 +1,91 @@
// utils/referral_validator.dart
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
class ReferralValidator {
static FormValidationErrors validateStep1(ReferralFormData formData) {
final errors = FormValidationErrors();
if (formData.requesterName.trim().isEmpty) {
errors.requesterName = 'Referral requester name is required';
}
if (formData.requesterPhone.trim().isEmpty) {
errors.requesterPhone = 'Phone number is required';
} else if (formData.countryEnum.countryCode == '966' &&
!_isValidSaudiPhone(formData.requesterPhone)) {
errors.requesterPhone = 'Please enter a valid Saudi phone number (5xxxxxxxx)';
}
if (formData.relationship == null) {
errors.relationship = 'Please select a relationship';
}
if (formData.relationship != null &&
formData.relationship?.iD == 5 &&
formData.otherRelationshipName.trim().isEmpty) {
errors.otherRelationshipName = 'Other relationship name is required';
}
return errors;
}
static FormValidationErrors validateStep2(ReferralFormData formData) {
final errors = FormValidationErrors();
if (formData.patientIdentification.trim().isEmpty) {
errors.patientIdentification = 'Identification number is required';
}
if (formData.patientName.trim().isEmpty) {
errors.patientName = 'Patient name is required';
}
if (formData.patientPhone == null) {
errors.patientPhone = 'Please Enter patient phone number';
}
if (formData.patientCity == null) {
errors.patientCity = 'Please select patient city';
}
return errors;
}
static FormValidationErrors validateStep3(ReferralFormData formData) {
final errors = FormValidationErrors();
if (formData.medicalReportImages.isEmpty) {
errors.medicalReport = 'At least one medical report is required';
}
if (formData.branch == null) {
errors.branch = 'Please select a branch';
}
if (formData.isPatientInsured && formData.insuredPatientImages.isEmpty) {
errors.insuredDocument = 'Insurance document is required for insured patients';
}
return errors;
}
static bool _isValidSaudiPhone(String phone) {
final regex = RegExp(r'^5\d{8}$');
return regex.hasMatch(phone);
}
static bool hasErrors(FormValidationErrors errors) {
return errors.requesterName != null ||
errors.requesterPhone != null ||
errors.relationship != null ||
errors.otherRelationshipName != null ||
errors.patientIdentification != null ||
errors.patientName != null ||
errors.patientPhone != null ||
errors.patientCity != null ||
errors.medicalReport != null ||
errors.branch != null ||
errors.insuredDocument != null;
}
}

@ -0,0 +1,206 @@
// managers/referral_form_manager.dart
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/enums.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_all_cities_resp_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/relationship_type_resp_mode.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart';
class ReferralFormManager extends ChangeNotifier {
final ReferralFormData _formData = ReferralFormData();
final FormValidationErrors _errors = FormValidationErrors();
ReferralFormData get formData => _formData;
FormValidationErrors get errors => _errors;
// Field-specific update methods that don't notify listeners immediately
void updateRequesterName(String value) {
_formData.requesterName = value;
_clearError('requesterName');
}
void updateRequesterPhone(String value) {
_formData.requesterPhone = value;
_clearError('requesterPhone');
}
void updateCountryEnum(CountryEnum value) {
_formData.countryEnum = value;
}
void updateRelationship(GetAllRelationshipTypeResponseModel? value) {
_formData.relationship = value;
_clearError('relationship');
}
void updateOtherRelationshipName(String value) {
_formData.otherRelationshipName = value;
_clearError('otherRelationshipName');
}
void updatePatientIdentification(String value) {
_formData.patientIdentification = value;
_clearError('patientIdentification');
}
void updatePatientName(String value) {
_formData.patientName = value;
_clearError('patientName');
}
void updatePatientPhone(String? value) {
_formData.patientPhone = value!;
_clearError('patientPhone');
}
void updatePatientCity(GetAllCitiesResponseModel? value) {
_formData.patientCity = value;
_clearError('patientCity');
}
void updateBranch(HospitalsModel? value) {
_formData.branch = value;
_clearError('branch');
}
void updateIsPatientInsured(bool value) {
_formData.isPatientInsured = value;
if (!value) {
_formData.insuredPatientImages.clear();
}
_clearError('insuredDocument');
}
void addMedicalReport(EReferralAttachment attachment) {
_formData.medicalReportImages.add(attachment);
_clearError('medicalReport');
}
void removeMedicalReport(int index) {
if (index >= 0 && index < _formData.medicalReportImages.length) {
_formData.medicalReportImages.removeAt(index);
}
}
void addInsuranceDocument(EReferralAttachment attachment) {
_formData.insuredPatientImages.add(attachment);
_clearError('insuredDocument');
}
void removeInsuranceDocument(int index) {
if (index >= 0 && index < _formData.insuredPatientImages.length) {
_formData.insuredPatientImages.removeAt(index);
}
}
// Error management
void setErrors(FormValidationErrors newErrors) {
_errors.requesterName = newErrors.requesterName;
_errors.requesterPhone = newErrors.requesterPhone;
_errors.relationship = newErrors.relationship;
_errors.otherRelationshipName = newErrors.otherRelationshipName;
_errors.patientIdentification = newErrors.patientIdentification;
_errors.patientName = newErrors.patientName;
_errors.patientPhone = newErrors.patientPhone;
_errors.patientCity = newErrors.patientCity;
_errors.medicalReport = newErrors.medicalReport;
_errors.branch = newErrors.branch;
_errors.insuredDocument = newErrors.insuredDocument;
notifyListeners(); // Only notify when errors change
}
void clearAllErrors() {
_errors.requesterName = null;
_errors.requesterPhone = null;
_errors.relationship = null;
_errors.otherRelationshipName = null;
_errors.patientIdentification = null;
_errors.patientName = null;
_errors.patientPhone = null;
_errors.patientCity = null;
_errors.medicalReport = null;
_errors.branch = null;
_errors.insuredDocument = null;
notifyListeners();
}
void _clearError(String field) {
bool shouldNotify = false;
switch (field) {
case 'requesterName':
if (_errors.requesterName != null) {
_errors.requesterName = null;
shouldNotify = true;
}
break;
case 'requesterPhone':
if (_errors.requesterPhone != null) {
_errors.requesterPhone = null;
shouldNotify = true;
}
break;
case 'relationship':
if (_errors.relationship != null) {
_errors.relationship = null;
shouldNotify = true;
}
break;
case 'otherRelationshipName':
if (_errors.otherRelationshipName != null) {
_errors.otherRelationshipName = null;
shouldNotify = true;
}
break;
case 'patientIdentification':
if (_errors.patientIdentification != null) {
_errors.patientIdentification = null;
shouldNotify = true;
}
break;
case 'patientName':
if (_errors.patientName != null) {
_errors.patientName = null;
shouldNotify = true;
}
break;
case 'patientPhone':
if (_errors.patientPhone != null) {
_errors.patientPhone = null;
shouldNotify = true;
}
break;
case 'patientCity':
if (_errors.patientCity != null) {
_errors.patientCity = null;
shouldNotify = true;
}
break;
case 'medicalReport':
if (_errors.medicalReport != null) {
_errors.medicalReport = null;
shouldNotify = true;
}
break;
case 'branch':
if (_errors.branch != null) {
_errors.branch = null;
shouldNotify = true;
}
break;
case 'insuredDocument':
if (_errors.insuredDocument != null) {
_errors.insuredDocument = null;
shouldNotify = true;
}
break;
}
if (shouldNotify) {
notifyListeners();
}
}
}

@ -1,15 +1,13 @@
import 'dart:ui';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.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/contact_us/contact_us_view_model.dart';
import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/new_referral.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/new_e_referral.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/search_e_referral.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
@ -35,7 +33,9 @@ class _EReferralPageState extends State<EReferralPage> {
}
bool isNewReferral = true;
VoidCallback? onNextStep;
VoidCallback? onNextStep;
int _currentPageIndex = 0;
int activeTabIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
@ -47,34 +47,53 @@ class _EReferralPageState extends State<EReferralPage> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 16.h),
contactUsVM.isHMGLocationsListLoading
? SizedBox.shrink()
: CustomTabBar(
CustomTabBar(
activeTextColor: AppColors.primaryRedColor,
activeBackgroundColor: AppColors.primaryRedColor.withValues(alpha: .1),
tabs: [
CustomTabBarModel(null, "New Referral".needTranslation),
CustomTabBarModel(null, "Search Referral".needTranslation),
],
onTabChange: (index) {},
onTabChange: (index) {
activeTabIndex =index;
setState(() {
});
},
).paddingSymmetrical(24.h, 0.h),
SizedBox(height: 24.h),
NewReferralPage(onNextStep: (nextStep) {
WidgetsBinding.instance.addPostFrameCallback((_) {
activeTabIndex ==0 ? NewReferralPage(
onNextStep: (nextStep) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
onNextStep = nextStep;
});
});
},
onStepChanged: (int value) {
setState(() {
onNextStep = nextStep;
});
});
})
_currentPageIndex = value;
});
},
) :
SearchEReferralPage(
onNextStep: (onNextStep) {
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
onNextStep = onNextStep;
});
});
},
)
],
);
}),
),
bottomNavigationBar: Padding(
padding: EdgeInsets.all(16.h),
padding: EdgeInsets.all(20.h),
child: CustomButton(
text: LocaleKeys.next.tr(),
onPressed:onNextStep ?? () {},
text:activeTabIndex == 0 ? _currentPageIndex >= 2 ? LocaleKeys.submit.tr() : LocaleKeys.next.tr() : LocaleKeys.search.tr(),
onPressed: onNextStep ?? () {},
backgroundColor: AppColors.primaryRedColor,
borderColor: AppColors.primaryRedColor,
textColor: AppColors.whiteColor,

@ -0,0 +1,183 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e-referral_otp.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_other_details.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_patient_info.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_requester_form.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/widget/e_referral_stepper.dart';
import 'package:provider/provider.dart';
import 'e-referral_validator.dart';
import 'e_referral_form_manager.dart';
class NewReferralPage extends StatefulWidget {
final Function(VoidCallback) onNextStep;
final Function(int) onStepChanged;
const NewReferralPage({
super.key,
required this.onNextStep,
required this.onStepChanged,
});
@override
State<NewReferralPage> createState() => _NewReferralPageState();
}
class _NewReferralPageState extends State<NewReferralPage> {
final PageController _pageController = PageController();
final ReferralFormManager _formManager = ReferralFormManager(); // Use manager
int _currentStep = 0;
final List<String> _steps = [
'Requester Info',
'Patient Information',
'Other details'
];
@override
void initState() {
super.initState();
_loadData();
widget.onNextStep(_handleNextStep);
}
void _handleNextStep() {
switch (_currentStep) {
case 0:
if (_validateCurrentStep()) {
OTPService.openOTPScreen(
context: context,
phoneNumber: _formManager.formData.requesterPhone ?? '',
countryEnum: _formManager.formData.countryEnum,
onSuccess: _proceedToNextStep,
);
}
break;
case 1:
if (_validateCurrentStep()) {
_proceedToNextStep();
}
break;
case 2:
if (_validateCurrentStep()) {
_submitReferral();
}
break;
}
}
bool _validateCurrentStep() {
FormValidationErrors stepErrors;
switch (_currentStep) {
case 0:
stepErrors = ReferralValidator.validateStep1(_formManager.formData);
break;
case 1:
stepErrors = ReferralValidator.validateStep2(_formManager.formData);
break;
case 2:
stepErrors = ReferralValidator.validateStep3(_formManager.formData);
break;
default:
stepErrors = FormValidationErrors();
}
// Update errors through manager
_formManager.setErrors(stepErrors);
return !ReferralValidator.hasErrors(stepErrors);
}
void _proceedToNextStep() {
_pageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
setState(() {
_currentStep++;
});
widget.onStepChanged(_currentStep);
}
void _submitReferral() {
CreateEReferralRequestModel createReferrralRequestModel =
CreateEReferralRequestModel(
isInsuredPatient: _formManager.formData.isPatientInsured,
cityCode: _formManager.formData.patientCity!.iD!.toString(),
cityName: _formManager.formData.patientCity!.description,
requesterName: _formManager.formData.requesterName,
requesterContactNo: _formManager.formData.requesterPhone,
requesterRelationship: _formManager.formData.relationship?.iD,
otherRelationship: _formManager.formData.relationship!.iD.toString(),
fullName: _formManager.formData.patientName,
identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'),
patientMobileNumber: _formManager.formData.patientPhone,
preferredBranchCode: _formManager.formData.branch!.iD,
medicalReportAttachment: _formManager.formData.medicalReportImages,
insuranceCardAttachment: _formManager.formData.insuredPatientImages,
preferredBranchName: _formManager.formData.branch!.desciption
);
final hmgServicesVM = context.read<HmgServicesViewModel>();
hmgServicesVM.createEReferral(
requestModel: createReferrralRequestModel,
onSuccess: (response) {
print("E-Referral submitted successfully");
},
onError: (errorMessage) {
// Handle error (e.g., show error message)
print(errorMessage);
},
);
}
void _loadData() {
final authVM = context.read<AuthenticationViewModel>();
final habibWalletVM = context.read<HabibWalletViewModel>();
final hmgServicesVM = context.read<HmgServicesViewModel>();
hmgServicesVM.getRelationshipType();
authVM.loadCountriesData();
hmgServicesVM.getAllCities();
habibWalletVM.getProjectsList();
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: _formManager,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
const SizedBox(height: 8),
ProgressStepperWidget(
currentStep: _currentStep,
steps: _steps,
),
Expanded(
child: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
onPageChanged: (index) => setState(() => _currentStep = index),
children: [
RequesterFormStep(),
PatientInformationStep(),
OtherDetailsStep(),
],
),
),
],
),
),
);
}
}

@ -1,432 +0,0 @@
// dart
// File: lib/presentation/e_referral/new_referral_page.dart
import 'dart:io';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/app_export.dart';
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/common_models/nationality_country_model.dart';
import 'package:hmg_patient_app_new/core/dependencies.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/features/hmg_services/models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
import 'package:hmg_patient_app_new/widgets/image_picker.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart';
class NewReferralPage extends StatefulWidget {
final Function(VoidCallback) onNextStep;
const NewReferralPage({super.key, required this.onNextStep});
@override
State<NewReferralPage> createState() => NewReferralPageState();
}
class NewReferralPageState extends State<NewReferralPage> {
final PageController _pageController = PageController();
int pageIndex = 0;
int _tabIndex = 0;
bool isPatientInsured =false;
final TextEditingController _nameController = TextEditingController();
final TextEditingController _phoneController = TextEditingController();
String _country = 'Saudi Arabia';
String? _relationship;
List<EReferralAttachment> medicalReportImages = [];
List<EReferralAttachment> insuredPatientImages = [];
void nextPressed() {
if (pageIndex < 2) {
_pageController.nextPage(duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
} else {
// submit logic
}
}
@override
initState() {
super.initState();
widget.onNextStep((){
nextPressed();
});
}
Widget _progressStep({required String title, required bool active, bool showDivider = true}) {
final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400;
return Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
radius: 13,
backgroundColor: active ? activeColor : Colors.grey.shade300,
child: Icon(Icons.check, size: 14, color: Colors.white),
),
if (showDivider)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Divider(thickness: 1),
),
],
),
const SizedBox(height: 6),
Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)),
const SizedBox(height: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
decoration: BoxDecoration(
color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100,
borderRadius: BorderRadius.circular(6),
),
child: Text(active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey)),
),
],
),
);
}
Widget _requesterForm() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
const SizedBox(height: 12),
const Text('Referral requester information',
style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16)).paddingSymmetrical(4.h, 0.h),
const SizedBox(height: 12),
TextInputWidget(
controller: _nameController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Enter Referral Requester Name*', labelText: 'Requester Name',
).paddingSymmetrical(0.h, 4.h),
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
selector: (context, authViewModel) {
final appState = getIt.get<AppState>();
return (
countriesList: authViewModel.countriesList,
selectedCountry: authViewModel.pickedCountryByUAEUser,
isArabic: appState.isArabic(),
);
},
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
builder: (context, data, child) {
final authVM = context.read<AuthenticationViewModel>();
return DropdownWidget(
labelText: LocaleKeys.country.tr(),
hintText:_country,
isEnable: true,
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
selectedValue: data.selectedCountry != null
? data.isArabic
? data.selectedCountry!.nameN ?? ""
: data.selectedCountry!.name ?? ""
: "",
onChange: authVM.onUAEUserCountrySelection,
hasSelectionCustomIcon: true,
labelColor: AppColors.textColor,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.globe,
).withVerticalPadding(8);
},
).paddingSymmetrical(0.h, 4.h),
TextInputWidget(
labelText: LocaleKeys.mobileNumber.tr(),
hintText: LocaleKeys.mobileNumber.tr(),
controller: null,
isEnable: true,
prefix: null,
isAllowLeadingIcon: true,
labelColor: AppColors.textColor, padding: const EdgeInsets.symmetric(horizontal: 16.0),
isReadOnly: true,
leadingIcon: AppAssets.call).paddingSymmetrical(0.h, 4.h),
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
selector: (context, authViewModel) {
final appState = getIt.get<AppState>();
return (
countriesList: authViewModel.countriesList,
selectedCountry: authViewModel.pickedCountryByUAEUser,
isArabic: appState.isArabic(),
);
},
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
builder: (context, data, child) {
final authVM = context.read<AuthenticationViewModel>();
return DropdownWidget(
labelText: "Relationship",
hintText: "Relationship*".needTranslation,
isEnable: true,
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
selectedValue: data.selectedCountry != null
? data.isArabic
? data.selectedCountry!.nameN ?? ""
: data.selectedCountry!.name ?? ""
: "",
onChange: authVM.onUAEUserCountrySelection,
hasSelectionCustomIcon: true,
labelColor: AppColors.textColor,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.globe,
).withVerticalPadding(8);
},
).paddingSymmetrical(0.h, 4.h),
const SizedBox(height: 120),
],
),
);
}
Widget _patientInformation(){
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
const SizedBox(height: 12),
'Patient information'.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h),
const SizedBox(height: 12),
TextInputWidget(
controller: _nameController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Enter Identification Number*', labelText: 'Identification Number ',
).paddingSymmetrical(0.h, 4.h),
TextInputWidget(
controller: _nameController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Patient Name*', labelText: 'Name',
).paddingSymmetrical(0.h, 4.h),
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
selector: (context, authViewModel) {
final appState = getIt.get<AppState>();
return (
countriesList: authViewModel.countriesList,
selectedCountry: authViewModel.pickedCountryByUAEUser,
isArabic: appState.isArabic(),
);
},
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
builder: (context, data, child) {
final authVM = context.read<AuthenticationViewModel>();
return DropdownWidget(
labelText: LocaleKeys.country.tr(),
hintText:_country,
isEnable: true,
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
selectedValue: data.selectedCountry != null
? data.isArabic
? data.selectedCountry!.nameN ?? ""
: data.selectedCountry!.name ?? ""
: "",
onChange: authVM.onUAEUserCountrySelection,
hasSelectionCustomIcon: true,
labelColor: AppColors.textColor,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.globe,
).withVerticalPadding(8);
},
).paddingSymmetrical(0.h, 4.h),
'Where the patient located'.needTranslation.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h),
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
selector: (context, authViewModel) {
final appState = getIt.get<AppState>();
return (
countriesList: authViewModel.countriesList,
selectedCountry: authViewModel.pickedCountryByUAEUser,
isArabic: appState.isArabic(),
);
},
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
builder: (context, data, child) {
final authVM = context.read<AuthenticationViewModel>();
return DropdownWidget(
labelText: LocaleKeys.country.tr(),
hintText:_country,
isEnable: true,
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
selectedValue: data.selectedCountry != null
? data.isArabic
? data.selectedCountry!.nameN ?? ""
: data.selectedCountry!.name ?? ""
: "",
onChange: authVM.onUAEUserCountrySelection,
hasSelectionCustomIcon: true,
labelColor: AppColors.textColor,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.globe,
).withVerticalPadding(8);
},
).paddingSymmetrical(0.h, 4.h),
]));
}
Widget _otherDetails() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
const SizedBox(height: 12),
'Other Details'.toText16(weight: FontWeight.bold).paddingSymmetrical(4.h, 0.h),
const SizedBox(height: 12),
InkWell(child: TextInputWidget(
controller: _nameController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Medical Report', labelText: 'Select Attachment',
suffix: Icon(Icons.attachment),
isReadOnly: true,
),
onTap: (){
ImageOptions.showImageOptionsNew(
context,
true,
(String image, File file) {
setState(() {
EReferralAttachment eReferralAttachment = new EReferralAttachment(fileName: 'image ${medicalReportImages.length + 1}.png', base64String: image);
medicalReportImages.add(eReferralAttachment);
});
},
);
},
).paddingSymmetrical(0.h, 4.h),
Selector<AuthenticationViewModel, ({List<NationalityCountries>? countriesList, NationalityCountries? selectedCountry, bool isArabic})>(
selector: (context, authViewModel) {
final appState = getIt.get<AppState>();
return (
countriesList: authViewModel.countriesList,
selectedCountry: authViewModel.pickedCountryByUAEUser,
isArabic: appState.isArabic(),
);
},
shouldRebuild: (previous, next) => previous.countriesList != next.countriesList || previous.selectedCountry != next.selectedCountry || previous.isArabic != next.isArabic,
builder: (context, data, child) {
final authVM = context.read<AuthenticationViewModel>();
return DropdownWidget(
labelText: LocaleKeys.branch.tr(),
hintText:_country,
isEnable: true,
dropdownItems: (data.countriesList ?? []).map((e) => data.isArabic ? e.nameN ?? "" : e.name ?? "").toList(),
selectedValue: data.selectedCountry != null
? data.isArabic
? data.selectedCountry!.nameN ?? ""
: data.selectedCountry!.name ?? ""
: "",
onChange: authVM.onUAEUserCountrySelection,
hasSelectionCustomIcon: true,
labelColor: AppColors.textColor,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.hospital,
).withVerticalPadding(8);
},
).paddingSymmetrical(0.h, 4.h),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
Checkbox(
value: isPatientInsured,
activeColor: AppColors.primaryRedColor,
onChanged: (bool? newValue) {
setState(() {
isPatientInsured = newValue!;
});
}),
Padding(
padding: const EdgeInsets.all(5.0),
child: Text(
"Patient is Insured".needTranslation,
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
),
],
),
],
).paddingSymmetrical(0.h, 4.h),
isPatientInsured? InkWell(child: TextInputWidget(
controller: _nameController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Insurance Document', labelText: 'Select Attachment',
suffix: Icon(Icons.attachment),
isReadOnly: true,
)) : SizedBox(),
]));
}
@override
Widget build(BuildContext context) {
final bool step0Active = pageIndex == 0;
final bool step1Active = pageIndex == 1;
final bool step2Active = pageIndex == 2;
return SizedBox(
height: MediaQuery
.of(context)
.size
.height, // constrain height
child: Column(
children: [
const SizedBox(height: 8),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
_progressStep(title: 'Requester Info', active: step0Active),
_progressStep(title: 'Patient Information', active: step1Active),
_progressStep(
title: 'Other details', active: step2Active, showDivider: false),
],
),
),
Expanded(
child: PageView(
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
onPageChanged: (i) => setState(() => pageIndex = i),
children: [
_requesterForm(),
_patientInformation(),
// const Center(child: Text('Patient Info - step 2 (placeholder)')),
_otherDetails(),
],
),
),
],
),
);
}
}

@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/widget/search_e_referral_form.dart';
import 'package:provider/provider.dart';
import 'e-referral_validator.dart';
import 'e_referral_form_manager.dart';
class SearchEReferralPage extends StatefulWidget {
final Function(VoidCallback) onNextStep;
const SearchEReferralPage({
super.key,
required this.onNextStep,
});
@override
State<SearchEReferralPage> createState() => _SearchEReferralPageState();
}
class _SearchEReferralPageState extends State<SearchEReferralPage> {
final PageController _pageController = PageController();
final ReferralFormManager _formManager = ReferralFormManager(); // Use manager
int _currentStep = 0;
@override
void initState() {
super.initState();
_loadData();
widget.onNextStep(_handleNextStep);
}
void _handleNextStep() {
_searchReferral();
}
void _searchReferral() {
CreateEReferralRequestModel createReferrralRequestModel =
CreateEReferralRequestModel(
isInsuredPatient: _formManager.formData.isPatientInsured,
cityCode: _formManager.formData.patientCity!.iD!.toString(),
cityName: _formManager.formData.patientCity!.description,
requesterName: _formManager.formData.requesterName,
requesterContactNo: _formManager.formData.requesterPhone,
requesterRelationship: _formManager.formData.relationship?.iD,
otherRelationship: _formManager.formData.relationship!.iD.toString(),
fullName: _formManager.formData.patientName,
identificationNo: int.tryParse(_formManager.formData!.patientIdentification ?? '0'),
patientMobileNumber: _formManager.formData.patientPhone,
preferredBranchCode: _formManager.formData.branch!.iD,
medicalReportAttachment: _formManager.formData.medicalReportImages,
insuranceCardAttachment: _formManager.formData.insuredPatientImages,
preferredBranchName: _formManager.formData.branch!.desciption
);
final hmgServicesVM = context.read<HmgServicesViewModel>();
hmgServicesVM.createEReferral(
requestModel: createReferrralRequestModel,
onSuccess: (response) {
print("E-Referral submitted successfully");
},
onError: (errorMessage) {
// Handle error (e.g., show error message)
print(errorMessage);
},
);
}
void _loadData() {
}
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider.value(
value: _formManager,
child: SizedBox(
height: MediaQuery.of(context).size.height,
child: Column(
children: [
Expanded(
child:
SearchEReferralFormForm(),
),
],
),
),
);
}
}

@ -0,0 +1,105 @@
// services/otp_service.dart
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:provider/provider.dart';
import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/check_activation_e_referral_req_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/send_activation_code_ereferral_req_model.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/features/authentication/widgets/otp_verification_screen.dart';
class OTPService {
static void openOTPScreen({
required BuildContext context,
required String phoneNumber,
required CountryEnum countryEnum,
required Function onSuccess,
}) {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
hmgServicesViewModel.eReferralSendActivationCode(
requestModel: SendActivationCodeForEReferralRequestModel(
patientMobileNumber: int.parse(phoneNumber),
zipCode: countryEnum.countryCode,
patientOutSA: countryEnum.countryCode == '966' ? 0 : 1,
),
onSuccess: (GenericApiModel response) {
_showOTPVerificationSheet(
context: context,
phoneNumber: phoneNumber,
loginTokenID: response.data,
onSuccess: onSuccess,
);
},
onError: (String errorMessage) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage))
);
},
);
}
static void _showOTPVerificationSheet({
required BuildContext context,
required String phoneNumber,
required String loginTokenID,
required Function onSuccess,
}) {
showCommonBottomSheet(
context,
isFullScreen: true,
title: "OTP Verification",
isCloseButtonVisible: false,
height:ResponsiveExtension.screenHeight * 0.75,
child: OTPVerificationScreen(
phoneNumber: phoneNumber,
checkActivationCode: (int code) {
_verifyOTP(
context: context,
loginTokenID: loginTokenID,
code: code,
onSuccess: onSuccess,
);
},
onResendOTPPressed: (String phoneNumber) {
Navigator.pop(context);
openOTPScreen(
context: context,
phoneNumber: phoneNumber,
countryEnum: CountryEnum.saudiArabia,
onSuccess: onSuccess,
);
},
isFormFamilyFile: false,
),
);
}
static void _verifyOTP({
required BuildContext context,
required String loginTokenID,
required int code,
required Function onSuccess,
}) {
final hmgServicesViewModel = context.read<HmgServicesViewModel>();
hmgServicesViewModel.checkEReferralActivationCode(
requestModel: CheckActivationCodeForEReferralRequestModel(
logInTokenID: loginTokenID,
activationCode: code.toString(),
),
onSuccess: (GenericApiModel response) {
Navigator.pop(context);
onSuccess();
},
onError: (String errorMessage) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(errorMessage))
);
},
);
}
}

@ -0,0 +1,308 @@
// widgets/other_details_step.dart
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/create_e_referral_model.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
import 'package:hmg_patient_app_new/widgets/image_picker.dart';
class OtherDetailsStep extends StatefulWidget {
const OtherDetailsStep({super.key});
@override
State<OtherDetailsStep> createState() => _OtherDetailsStepState();
}
class _OtherDetailsStepState extends State<OtherDetailsStep> {
final TextEditingController _medicalReportController = TextEditingController();
final TextEditingController _insuranceController = TextEditingController();
late ReferralFormManager _formManager;
@override
void initState() {
super.initState();
_formManager = context.read<ReferralFormManager>();
_updateMedicalReportText();
_updateInsuranceText();
}
@override
void didUpdateWidget(OtherDetailsStep oldWidget) {
super.didUpdateWidget(oldWidget);
_updateMedicalReportText();
_updateInsuranceText();
}
void _updateMedicalReportText() {
final hasMedicalReports = _formManager.formData.medicalReportImages.isNotEmpty;
_medicalReportController.text = hasMedicalReports
? '${_formManager.formData.medicalReportImages.length} file(s) selected'
: '';
}
void _updateInsuranceText() {
final hasInsuranceDocs = _formManager.formData.insuredPatientImages.isNotEmpty;
_insuranceController.text = hasInsuranceDocs
? '${_formManager.formData.insuredPatientImages.length} file(s) selected'
: '';
}
@override
Widget build(BuildContext context) {
return Consumer<ReferralFormManager>(
builder: (context, formManager, child) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
const SizedBox(height: 12),
_buildSectionTitle('Other Details'),
const SizedBox(height: 12),
_buildMedicalReportField(formManager),
_buildBranchField(context, formManager),
_buildInsuranceCheckbox(formManager),
if (formManager.formData.isPatientInsured) _buildInsuranceField(formManager),
],
),
);
},
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16)
).paddingSymmetrical(4, 0);
}
Widget _buildMedicalReportField(ReferralFormManager formManager) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
child: TextInputWidget(
controller: _medicalReportController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Medical Report',
labelText: 'Select Attachment',
suffix: const Icon(Icons.attachment),
isReadOnly: true,
errorMessage: formManager.errors.medicalReport,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.medicalReport),
),
onTap: () {
ImageOptions.showImageOptionsNew(
context,
true,
(String image, File file) {
_addMedicalReport(image, file, formManager);
},
);
},
),
if (formManager.formData.medicalReportImages.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Wrap(
spacing: 8.0,
children: formManager.formData.medicalReportImages.asMap().entries.map((entry) {
final index = entry.key;
return Chip(
label: Text('Medical Report ${index + 1}'),
deleteIcon: const Icon(Icons.close, size: 16),
onDeleted: () {
_removeMedicalReport(index, formManager);
},
);
}).toList(),
),
),
],
).paddingSymmetrical(0, 4);
}
Widget _buildBranchField(BuildContext context, ReferralFormManager formManager) {
return DropdownWidget(
labelText: 'Branch',
hintText: formManager.formData.branch?.desciption ?? "Select Branch",
isEnable: false,
hasSelectionCustomIcon: true,
labelColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.hospital,
dropdownItems: [],
errorMessage: formManager.errors.branch,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.branch),
).paddingSymmetrical(0, 4).onPress(() {
_showBranchBottomSheet(context, formManager);
});
}
Widget _buildInsuranceCheckbox(ReferralFormManager formManager) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Row(
children: [
Checkbox(
value: formManager.formData.isPatientInsured,
activeColor: Colors.red,
onChanged: (bool? newValue) {
final value = newValue ?? false;
formManager.updateIsPatientInsured(value);
if (!value) {
_updateInsuranceText();
}
},
),
const Padding(
padding: EdgeInsets.all(5.0),
child: Text(
"Patient is Insured",
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w600),
),
),
],
),
],
).paddingSymmetrical(0, 4);
}
Widget _buildInsuranceField(ReferralFormManager formManager) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
child: TextInputWidget(
controller: _insuranceController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Insurance Document',
labelText: 'Select Attachment',
suffix: const Icon(Icons.attachment),
isReadOnly: true,
errorMessage: formManager.errors.insuredDocument,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.insuredDocument),
),
onTap: () {
ImageOptions.showImageOptionsNew(
context,
true,
(String image, File file) {
_addInsuranceDocument(image, file, formManager);
},
);
},
),
if (formManager.formData.insuredPatientImages.isNotEmpty)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: Wrap(
spacing: 8.0,
children: formManager.formData.insuredPatientImages.asMap().entries.map((entry) {
final index = entry.key;
return Chip(
label: Text('Insurance ${index + 1}'),
deleteIcon: const Icon(Icons.close, size: 16),
onDeleted: () {
_removeInsuranceDocument(index, formManager);
},
);
}).toList(),
),
),
],
);
}
void _showBranchBottomSheet(BuildContext context, ReferralFormManager formManager) {
final habibWalletVM = context.read<HabibWalletViewModel>();
showCommonBottomSheetWithoutHeight(
context,
title: "Select Branch",
child: Consumer<HabibWalletViewModel>(
builder: (context, habibWalletVM, child) {
final hospitals = habibWalletVM.advancePaymentHospitals;
if (hospitals == null || hospitals.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text('No branches available'),
),
);
}
return ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final branch = hospitals[index];
return ListTile(
title: Text(branch.desciption ?? 'Unknown'),
onTap: () {
formManager.updateBranch(branch);
Navigator.pop(context);
},
);
},
separatorBuilder: (context, index) => const Divider(),
itemCount: hospitals.length,
);
},
),
useSafeArea: true,
isFullScreen: false,
isCloseButtonVisible: true,
);
}
void _addMedicalReport(String image, File file, ReferralFormManager formManager) {
final newAttachment = EReferralAttachment(
fileName: 'medical_report_${formManager.formData.medicalReportImages.length + 1}.png',
base64String: image
);
formManager.addMedicalReport(newAttachment);
_updateMedicalReportText();
}
void _removeMedicalReport(int index, ReferralFormManager formManager) {
formManager.removeMedicalReport(index);
_updateMedicalReportText();
}
void _addInsuranceDocument(String image, File file, ReferralFormManager formManager) {
final newAttachment = EReferralAttachment(
fileName: 'insurance_${formManager.formData.insuredPatientImages.length + 1}.png',
base64String: image
);
formManager.addInsuranceDocument(newAttachment);
_updateInsuranceText();
}
void _removeInsuranceDocument(int index, ReferralFormManager formManager) {
formManager.removeInsuranceDocument(index);
_updateInsuranceText();
}
@override
void dispose() {
_medicalReportController.dispose();
_insuranceController.dispose();
super.dispose();
}
}

@ -0,0 +1,289 @@
// widgets/patient_information_step.dart
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
class PatientInformationStep extends StatefulWidget {
const PatientInformationStep({super.key});
@override
State<PatientInformationStep> createState() => PatientInformationStepState();
}
class PatientInformationStepState extends State<PatientInformationStep> {
late TextEditingController _identificationController;
late TextEditingController _nameController;
late TextEditingController _phoneController;
late FocusNode _identificationFocusNode;
late FocusNode _nameFocusNode;
late FocusNode _phoneFocusNode;
late ReferralFormManager _formManager;
@override
void initState() {
super.initState();
_formManager = context.read<ReferralFormManager>();
_identificationController = TextEditingController();
_nameController = TextEditingController();
_phoneController = TextEditingController();
_identificationFocusNode = FocusNode();
_nameFocusNode = FocusNode();
_phoneFocusNode = FocusNode();
// Initialize controllers with current values
_identificationController.text = _formManager.formData.patientIdentification ?? '';
_nameController.text = _formManager.formData.patientName ?? '';
_phoneController.text = _formManager.formData.patientPhone ?? '';
// Auto-focus the identification field when the step loads
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
_identificationFocusNode.requestFocus();
}
});
}
@override
Widget build(BuildContext context) {
return Consumer<ReferralFormManager>(
builder: (context, formManager, child) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
const SizedBox(height: 12),
_buildSectionTitle('Patient information'),
const SizedBox(height: 12),
_buildIdentificationField(formManager),
_buildPatientNameField(formManager),
// _buildPatientCountryField(context, formManager),
_buildPatientPhoneField(formManager),
const SizedBox(height: 20),
_buildSectionTitle('Where the patient located'),
_buildPatientCityField(context, formManager),
],
),
);
},
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16)
).paddingSymmetrical(4, 0);
}
Widget _buildIdentificationField(ReferralFormManager formManager) {
return Focus(
focusNode: _identificationFocusNode,
child: TextInputWidget(
controller: _identificationController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Enter Identification Number*',
labelText: 'Identification Number',
errorMessage: formManager.errors.patientIdentification,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientIdentification),
onChange: (value) {
formManager.updatePatientIdentification(value ?? '');
},
onSubmitted: (value) {
_nameFocusNode.requestFocus();
},
).paddingSymmetrical(0, 4),
);
}
Widget _buildPatientNameField(ReferralFormManager formManager) {
return Focus(
focusNode: _nameFocusNode,
child: TextInputWidget(
controller: _nameController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Patient Name*',
labelText: 'Name',
keyboardType: TextInputType.text,
errorMessage: formManager.errors.patientName,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientName),
onChange: (value) {
formManager.updatePatientName(value ?? '');
},
onSubmitted: (value) {
// Optionally move to next field or keep focus
},
).paddingSymmetrical(0, 4),
);
}
// Widget _buildPatientCountryField(BuildContext context, ReferralFormManager formManager) {
// return DropdownWidget(
// labelText: 'Country',
// hintText: formManager.formData.patientCountry?.name ?? "Select Country",
// isEnable: false,
// hasSelectionCustomIcon: true,
// labelColor: Colors.black,
// padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
// selectionCustomIcon: AppAssets.arrow_down,
// leadingIcon: AppAssets.globe,
// dropdownItems: [],
// errorMessage: formManager.errors.patientCountry,
// hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientCountry),
// ).paddingSymmetrical(0, 4).onPress(() {
// _showCountryBottomSheet(context, formManager);
// });
// }
Widget _buildPatientPhoneField(ReferralFormManager formManager) {
return Focus(
focusNode: _phoneFocusNode,
child: TextInputWidget(
labelText: 'Phone Number',
hintText: "5xxxxxxxx",
controller: _phoneController,
padding: const EdgeInsets.all(8),
keyboardType: TextInputType.number,
onChange: (value) {
formManager.updatePatientPhone(value ?? '');
},
onCountryChange: (value) {
formManager.updateCountryEnum(value);
},
prefix: '966',
isBorderAllowed: false,
isAllowLeadingIcon: true,
fontSize: 13,
isCountryDropDown: true,
leadingIcon: AppAssets.smart_phone,
errorMessage: formManager.errors.patientPhone,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientPhone)
).paddingSymmetrical(0, 8),
);
}
Widget _buildPatientCityField(BuildContext context, ReferralFormManager formManager) {
return DropdownWidget(
labelText: 'City',
hintText: formManager.formData.patientCity?.description ?? "Select City",
isEnable: false,
hasSelectionCustomIcon: true,
labelColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
selectionCustomIcon: AppAssets.arrow_down,
leadingIcon: AppAssets.globe,
dropdownItems: [],
errorMessage: formManager.errors.patientCity,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.patientCity),
).paddingSymmetrical(0, 4).onPress(() {
_showCityBottomSheet(context, formManager);
});
}
// void _showCountryBottomSheet(BuildContext context, ReferralFormManager formManager) {
// final authVM = context.read<AuthenticationViewModel>();
//
// showCommonBottomSheetWithoutHeight(
// context,
// title: "Select Country",
// child: Consumer<AuthenticationViewModel>(
// builder: (context, authVM, child) {
// final countries = authVM.countriesList;
// if (countries == null || countries.isEmpty) {
// return const Center(
// child: Padding(
// padding: EdgeInsets.all(16.0),
// child: Text('No countries available'),
// ),
// );
// }
//
// return ListView.separated(
// shrinkWrap: true,
// physics: const BouncingScrollPhysics(),
// itemBuilder: (context, index) {
// final country = countries[index];
// return ListTile(
// title: Text(country.name ?? 'Unknown'),
// onTap: () {
// formManager.updatePatientCountry(country);
// Navigator.pop(context);
// },
// );
// },
// separatorBuilder: (context, index) => const Divider(),
// itemCount: countries.length,
// );
// },
// ),
// useSafeArea: true,
// isFullScreen: false,
// isCloseButtonVisible: true,
// );
// }
void _showCityBottomSheet(BuildContext context, ReferralFormManager formManager) {
final hmgServicesVM = context.read<HmgServicesViewModel>();
showCommonBottomSheetWithoutHeight(
context,
title: "Select City",
child: Consumer<HmgServicesViewModel>(
builder: (context, hmgServicesVM, child) {
final cities = hmgServicesVM.getAllCitiesList;
if (cities == null || cities.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text('No cities available'),
),
);
}
return ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final city = cities[index];
return ListTile(
title: Text(city.description ?? 'Unknown'),
onTap: () {
formManager.updatePatientCity(city);
Navigator.pop(context);
},
);
},
separatorBuilder: (context, index) => const Divider(),
itemCount: cities.length,
);
},
),
useSafeArea: true,
isFullScreen: false,
isCloseButtonVisible: true,
);
}
@override
void dispose() {
_identificationController.dispose();
_nameController.dispose();
_phoneController.dispose();
_identificationFocusNode.dispose();
_nameFocusNode.dispose();
_phoneFocusNode.dispose();
super.dispose();
}
}

@ -0,0 +1,228 @@
// widgets/requester_form_step.dart
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
// widgets/requester_form_step.dart
// widgets/requester_form_step.dart
class RequesterFormStep extends StatefulWidget {
const RequesterFormStep({super.key});
@override
State<RequesterFormStep> createState() => RequesterFormStepState();
}
class RequesterFormStepState extends State<RequesterFormStep> {
late TextEditingController _nameController;
late TextEditingController _phoneController;
late TextEditingController _otherNameController;
late FocusNode _nameFocusNode;
late FocusNode _phoneFocusNode;
late FocusNode _otherNameFocusNode;
late ReferralFormManager _formManager;
@override
void initState() {
super.initState();
_formManager = context.read<ReferralFormManager>();
_nameController = TextEditingController();
_phoneController = TextEditingController();
_otherNameController = TextEditingController();
_nameFocusNode = FocusNode();
_phoneFocusNode = FocusNode();
_otherNameFocusNode = FocusNode();
// Initialize controllers with current values
_nameController.text = _formManager.formData.requesterName ?? '';
_phoneController.text = _formManager.formData.requesterPhone ?? '';
_otherNameController.text = _formManager.formData.otherRelationshipName ?? '';
// Auto-focus the name field when the step loads
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
_nameFocusNode.requestFocus();
}
});
}
@override
Widget build(BuildContext context) {
return Consumer<ReferralFormManager>(
builder: (context, formManager, child) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
const SizedBox(height: 12),
_buildSectionTitle('Referral requester information'),
const SizedBox(height: 12),
_buildNameField(formManager),
_buildPhoneField(formManager),
_buildRelationshipField(context, formManager),
if (_showOtherNameField(formManager)) _buildOtherNameField(formManager),
],
),
);
},
);
}
Widget _buildSectionTitle(String title) {
return Text(
title,
style: const TextStyle(fontWeight: FontWeight.w700, fontSize: 16)
).paddingSymmetrical(4, 0);
}
Widget _buildNameField(ReferralFormManager formManager) {
return Focus(
focusNode: _nameFocusNode,
child: TextInputWidget(
controller: _nameController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Enter Referral Requester Name*',
labelText: 'Requester Name',
keyboardType: TextInputType.text,
errorMessage: formManager.errors.requesterName,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterName),
onChange: (value) {
formManager.updateRequesterName(value ?? '');
},
onSubmitted: (value) {
_phoneFocusNode.requestFocus();
},
).paddingSymmetrical(0, 8),
);
}
Widget _buildPhoneField(ReferralFormManager formManager) {
return Focus(
focusNode: _phoneFocusNode,
child: TextInputWidget(
labelText: 'Phone Number',
hintText: "5xxxxxxxx",
controller: _phoneController,
padding: const EdgeInsets.all(8),
keyboardType: TextInputType.number,
onChange: (value) {
formManager.updateRequesterPhone(value ?? '');
},
onCountryChange: (value) {
formManager.updateCountryEnum(value);
},
prefix: '966',
isBorderAllowed: false,
isAllowLeadingIcon: true,
fontSize: 13,
isCountryDropDown: true,
leadingIcon: AppAssets.smart_phone,
errorMessage: formManager.errors.requesterPhone,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone)
).paddingSymmetrical(0, 8),
);
}
Widget _buildRelationshipField(BuildContext context, ReferralFormManager formManager) {
return DropdownWidget(
labelText: "Relationship",
hintText: formManager.formData.relationship?.textEn ?? "Select Relation",
isEnable: false,
selectedValue: formManager.formData.relationship?.textEn ?? "Select Relation",
errorMessage: formManager.errors.relationship,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.relationship),
hasSelectionCustomIcon: false,
labelColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
leadingIcon: AppAssets.user_circle,
dropdownItems: [],
).paddingSymmetrical(0, 8).onPress(() {
_showRelationshipBottomSheet(context, formManager);
});
}
Widget _buildOtherNameField(ReferralFormManager formManager) {
return Focus(
focusNode: _otherNameFocusNode,
child: TextInputWidget(
controller: _otherNameController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText: 'Other Name*',
labelText: 'Other Name',
errorMessage: formManager.errors.otherRelationshipName,
onChange: (value) {
formManager.updateOtherRelationshipName(value ?? '');
},
).paddingSymmetrical(0, 4),
);
}
bool _showOtherNameField(ReferralFormManager formManager) {
return formManager.formData.relationship != null &&
formManager.formData.relationship?.iD == 5;
}
void _showRelationshipBottomSheet(BuildContext context, ReferralFormManager formManager) {
final hmgServicesVM = context.read<HmgServicesViewModel>();
showCommonBottomSheetWithoutHeight(
context,
title: "Select Relation",
child: Consumer<HmgServicesViewModel>(
builder: (context, hmgServicesVM, child) {
if (hmgServicesVM.relationTypes.isEmpty) {
return const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Text('No relationships available'),
),
);
}
return ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final relationship = hmgServicesVM.relationTypes[index];
return ListTile(
title: Text(relationship.textEn ?? 'Unknown'),
onTap: () {
formManager.updateRelationship(relationship);
Navigator.pop(context);
},
);
},
separatorBuilder: (context, index) => const Divider(),
itemCount: hmgServicesVM.relationTypes.length,
);
},
),
isFullScreen: false,
isCloseButtonVisible: true,
);
}
@override
void dispose() {
_nameController.dispose();
_phoneController.dispose();
_otherNameController.dispose();
_nameFocusNode.dispose();
_phoneFocusNode.dispose();
_otherNameFocusNode.dispose();
super.dispose();
}
}

@ -0,0 +1,72 @@
// widgets/progress_stepper_widget.dart
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/theme/colors.dart';
class ProgressStepperWidget extends StatelessWidget {
final int currentStep;
final List<String> steps;
const ProgressStepperWidget({
super.key,
required this.currentStep,
required this.steps,
});
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (int i = 0; i < steps.length; i++)
_buildStep(
title: steps[i],
active: i == currentStep,
showDivider: i < steps.length - 1,
),
],
),
);
}
Widget _buildStep({required String title, required bool active, bool showDivider = true}) {
final Color activeColor = active ? AppColors.primaryRedColor : Colors.grey.shade400;
return Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
radius: 13,
backgroundColor: active ? activeColor : Colors.grey.shade300,
child: Icon(Icons.check, size: 14, color: Colors.white),
),
if (showDivider)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Divider(thickness: 1),
),
],
),
const SizedBox(height: 6),
Text(title, style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600)),
const SizedBox(height: 6),
Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
decoration: BoxDecoration(
color: active ? activeColor.withOpacity(0.15) : Colors.grey.shade100,
borderRadius: BorderRadius.circular(6),
),
child: Text(
active ? 'Active' : 'Inactive',
style: TextStyle(fontSize: 9, color: active ? activeColor : Colors.grey)
),
),
],
),
);
}
}

@ -0,0 +1,184 @@
import 'package:flutter/material.dart';
import 'package:hmg_patient_app_new/core/utils/validation_utils.dart';
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
import 'package:hmg_patient_app_new/features/hmg_services/models/ui_models/e_referral_form_model.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e-referral_validator.dart';
import 'package:hmg_patient_app_new/presentation/e_referral/e_referral_form_manager.dart';
import 'package:provider/provider.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart';
import 'package:hmg_patient_app_new/widgets/dropdown/dropdown_widget.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart';
class SearchEReferralFormForm extends StatefulWidget {
const SearchEReferralFormForm({super.key});
@override
State<SearchEReferralFormForm> createState() => SearchEReferralFormFormState();
}
class SearchEReferralFormFormState extends State<SearchEReferralFormForm> {
late TextEditingController _searchController;
late TextEditingController _phoneController;
late FocusNode _searchFocusNode;
late FocusNode _phoneFocusNode;
int criteria =0;
List<Map<int,String>> criteriaList = [
{0: 'Identification Number'},
{1: 'Referral Number'},
];
late ReferralFormManager _formManager;
@override
void initState() {
super.initState();
_formManager = context.read<ReferralFormManager>();
_searchController = TextEditingController();
_phoneController = TextEditingController();
_searchFocusNode = FocusNode();
_phoneFocusNode = FocusNode();
// Initialize controllers with current values
_searchController.text = '';
_phoneController.text = '';
// Auto-focus the name field when the step loads
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
_searchFocusNode.requestFocus();
}
});
}
@override
Widget build(BuildContext context) {
return Consumer<ReferralFormManager>(
builder: (context, formManager, child) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: ListView(
physics: const BouncingScrollPhysics(),
children: [
const SizedBox(height: 12),
_buildSelectionField(context, formManager),
_buildNameField(formManager),
_buildPhoneField(formManager),
],
),
);
},
);
}
Widget _buildNameField(ReferralFormManager formManager) {
return Focus(
focusNode: _searchFocusNode,
child: TextInputWidget(
controller: _searchController,
padding: const EdgeInsets.symmetric(horizontal: 16.0),
hintText:criteria ==0 ? "Enter Identification Number" : "Enter Referral Number",
labelText: criteria ==0 ? "Identification Number" : "Referral Number",
keyboardType: TextInputType.text,
// errorMessage: formManager.errors.requesterName,
hasError: !ValidationUtils.isNullOrEmpty(_searchController.text),
onChange: (value) {
},
onSubmitted: (value) {
},
).paddingSymmetrical(0, 8),
);
}
Widget _buildPhoneField(ReferralFormManager formManager) {
return Focus(
focusNode: _phoneFocusNode,
child: TextInputWidget(
labelText: 'Phone Number',
hintText: "5xxxxxxxx",
controller: _phoneController,
padding: const EdgeInsets.all(8),
keyboardType: TextInputType.number,
onChange: (value) {
formManager.updateRequesterPhone(value ?? '');
},
onCountryChange: (value) {
formManager.updateCountryEnum(value);
},
prefix: '966',
isBorderAllowed: false,
isAllowLeadingIcon: true,
fontSize: 13,
isCountryDropDown: true,
leadingIcon: AppAssets.smart_phone,
errorMessage: formManager.errors.requesterPhone,
hasError: !ValidationUtils.isNullOrEmpty(formManager.errors.requesterPhone)
).paddingSymmetrical(0, 8),
);
}
Widget _buildSelectionField(BuildContext context, ReferralFormManager formManager) {
return DropdownWidget(
labelText: "Select the Search Criteria",
hintText: criteria ==0 ? "Identification Number" : "Referral Number",
isEnable: false,
hasSelectionCustomIcon: false,
labelColor: Colors.black,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
leadingIcon: AppAssets.search_icon,
dropdownItems: [],
).paddingSymmetrical(0, 8).onPress(() {
_showCriteriaBottomSheet(context);
});
}
void _showCriteriaBottomSheet(BuildContext context,) {
showCommonBottomSheetWithoutHeight(
context,
title: "Select Criteria",
child: ListView.separated(
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
itemBuilder: (context, index) {
return ListTile(
title: Text(criteriaList[index].values.first),
onTap: () {
setState(() {
criteria = index;
});
Navigator.pop(context);
},
);
},
separatorBuilder: (context, index) => const Divider(),
itemCount: criteriaList.length,
),
isFullScreen: false,
isCloseButtonVisible: true,
);
}
@override
void dispose() {
_searchController.dispose();
_phoneController.dispose();
_searchFocusNode.dispose();
_phoneFocusNode.dispose();
super.dispose();
}
}

@ -45,7 +45,7 @@ class AttachmentOptions extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SvgPicture.asset(
"assets/images/$icon",
"assets/images/svg/$icon",
),
title.toText11(isBold: true),
],

@ -19,7 +19,8 @@ class DropdownWidget extends StatelessWidget {
final String? selectionCustomIcon;
final String? leadingIcon;
final Color? labelColor;
final String? errorMessage;
final bool? hasError;
const DropdownWidget(
{Key? key,
required this.labelText,
@ -34,7 +35,10 @@ class DropdownWidget extends StatelessWidget {
this.hasSelectionCustomIcon = false,
this.selectionCustomIcon,
this.leadingIcon,
this.labelColor})
this.labelColor,
this.errorMessage,
this.hasError =false
})
: super(key: key);
@override
@ -42,16 +46,18 @@ class DropdownWidget extends StatelessWidget {
Widget content = Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [_buildLabelText(labelColor), _buildDropdown(context)],
children: [_buildLabelText(labelColor), _buildDropdown(context),],
);
return Container(
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Container(
padding: padding,
alignment: Alignment.center, // This might need adjustment based on layout
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
borderRadius: isAllowRadius ? 15.h : null,
side: isBorderAllowed ? BorderSide(color: const Color(0xffefefef), width: 1) : null,
side: isBorderAllowed ? BorderSide(color: hasError! ? Colors.red: const Color(0xffefefef), width: 1) : null,
),
child: Row(
// Wrap with a Row
@ -62,9 +68,21 @@ class DropdownWidget extends StatelessWidget {
SizedBox(width: 3.h),
],
Expanded(child: content),
],
),
);
),
if (hasError! && errorMessage != null)
Padding(
padding: EdgeInsets.only(top: 4.h, left: 12.h), // Adjust padding as needed
child: Text(
errorMessage!,
style: TextStyle(
color: Colors.red,
fontSize: 12.f,
),
),
)]);
}
Widget _buildLeadingIcon() {

Loading…
Cancel
Save