Ambulance order screens and geocoding request added
parent
5aef8d67de
commit
1ca39e1332
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="10" height="13" viewBox="0 0 10 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 2.85449C3.79188 2.85449 2.8125 3.83387 2.8125 5.04199C2.8125 6.25011 3.79188 7.22949 5 7.22949C6.20812 7.22949 7.1875 6.25011 7.1875 5.04199C7.1875 3.83387 6.20812 2.85449 5 2.85449ZM3.6875 5.04199C3.6875 4.31712 4.27513 3.72949 5 3.72949C5.72487 3.72949 6.3125 4.31712 6.3125 5.04199C6.3125 5.76687 5.72487 6.35449 5 6.35449C4.27513 6.35449 3.6875 5.76687 3.6875 5.04199Z" fill="#ED1C2B"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 0.229492C2.38798 0.229492 0.1875 2.42783 0.1875 5.09268C0.1875 7.8228 2.43772 9.64709 4.31579 10.8329L4.32334 10.8377L4.33107 10.8422C4.5346 10.9593 4.76514 11.0212 5 11.0212C5.23486 11.0212 5.4654 10.9593 5.66893 10.8422L5.67567 10.8383L5.68227 10.8342C7.56769 9.65737 9.8125 7.81358 9.8125 5.09268C9.8125 2.42783 7.61202 0.229492 5 0.229492ZM1.0625 5.09268C1.0625 2.90553 2.87676 1.10449 5 1.10449C7.12324 1.10449 8.9375 2.90553 8.9375 5.09268C8.9375 7.29808 7.10717 8.91207 5.22662 10.0871C5.15694 10.1259 5.079 10.1462 5 10.1462C4.92132 10.1462 4.84369 10.1261 4.77422 10.0876C2.89372 8.89884 1.0625 7.30546 1.0625 5.09268Z" fill="#ED1C2B"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
@ -0,0 +1,4 @@
|
|||||||
|
enum AmbulanceCallingPlace{
|
||||||
|
FROM_HOSPITAL,
|
||||||
|
TO_HOSPITAL
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
enum AmbulanceDirection{
|
||||||
|
ONE_WAY, TWO_WAY
|
||||||
|
}
|
||||||
@ -0,0 +1,205 @@
|
|||||||
|
|
||||||
|
import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/PatientERTransportationMethod.dart' show PatientERTransportationMethod;
|
||||||
|
|
||||||
|
class PatientER_RC {
|
||||||
|
double? versionID;
|
||||||
|
int? channel;
|
||||||
|
int? languageID;
|
||||||
|
String? iPAdress;
|
||||||
|
String? generalid;
|
||||||
|
bool? patientOutSA;
|
||||||
|
String? sessionID;
|
||||||
|
bool? isDentalAllowedBackend;
|
||||||
|
int? deviceTypeID;
|
||||||
|
int? patientID;
|
||||||
|
String? tokenID;
|
||||||
|
int? patientTypeID;
|
||||||
|
int? patientType;
|
||||||
|
int? orderServiceID;
|
||||||
|
String? patientIdentificationID;
|
||||||
|
dynamic patientOutSa;
|
||||||
|
int? projectID;
|
||||||
|
int? lineItemNo;
|
||||||
|
TransportationDetails? transportationDetails;
|
||||||
|
PatientERTransportationMethod? patientERTransportationMethod;
|
||||||
|
|
||||||
|
PatientER_RC(
|
||||||
|
{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,
|
||||||
|
this.orderServiceID,
|
||||||
|
this.patientIdentificationID,
|
||||||
|
this.patientOutSa,
|
||||||
|
this.projectID,
|
||||||
|
this.lineItemNo,
|
||||||
|
this.transportationDetails});
|
||||||
|
|
||||||
|
PatientER_RC.fromJson(Map<String, dynamic> json) {
|
||||||
|
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'];
|
||||||
|
orderServiceID = json['OrderServiceID'];
|
||||||
|
patientIdentificationID = json['PatientIdentificationID'];
|
||||||
|
patientOutSa = json['patientOutSa'];
|
||||||
|
projectID = json['projectID'];
|
||||||
|
lineItemNo = json['lineItemNo'];
|
||||||
|
transportationDetails = json['transportationDetails'] != null
|
||||||
|
? new TransportationDetails.fromJson(json['transportationDetails'])
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
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;
|
||||||
|
data['OrderServiceID'] = this.orderServiceID;
|
||||||
|
data['PatientIdentificationID'] = this.patientIdentificationID;
|
||||||
|
data['patientOutSa'] = this.patientOutSa;
|
||||||
|
data['projectID'] = this.projectID;
|
||||||
|
data['lineItemNo'] = this.lineItemNo;
|
||||||
|
if (this.transportationDetails != null) {
|
||||||
|
data['transportationDetails'] = this.transportationDetails!.toJson();
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TransportationDetails {
|
||||||
|
int? direction;
|
||||||
|
int? haveAppointment;
|
||||||
|
int? tripType;
|
||||||
|
int? pickupUrgency;
|
||||||
|
int? pickupSpot;
|
||||||
|
String? pickupDateTime;
|
||||||
|
String? transportationType;
|
||||||
|
int? ambulate;
|
||||||
|
String? ambulateTitle;
|
||||||
|
String? notes;
|
||||||
|
int? requesterFileNo;
|
||||||
|
String? requesterMobileNo;
|
||||||
|
bool? requesterIsOutSA;
|
||||||
|
String? pickupLocationName;
|
||||||
|
String? dropoffLocationName;
|
||||||
|
String? pickupLatitude;
|
||||||
|
String? pickupLongitude;
|
||||||
|
String? dropoffLatitude;
|
||||||
|
String? dropoffLongitude;
|
||||||
|
String? appointmentNo;
|
||||||
|
String? appointmentClinicName;
|
||||||
|
String? appointmentDoctorName;
|
||||||
|
String? appointmentBranch;
|
||||||
|
String? appointmentTime;
|
||||||
|
|
||||||
|
TransportationDetails(
|
||||||
|
{this.direction,
|
||||||
|
this.haveAppointment,
|
||||||
|
this.tripType,
|
||||||
|
this.pickupUrgency,
|
||||||
|
this.pickupSpot,
|
||||||
|
this.pickupDateTime,
|
||||||
|
this.transportationType,
|
||||||
|
this.ambulate,
|
||||||
|
this.ambulateTitle,
|
||||||
|
this.notes,
|
||||||
|
this.requesterFileNo,
|
||||||
|
this.requesterMobileNo,
|
||||||
|
this.requesterIsOutSA,
|
||||||
|
this.pickupLocationName,
|
||||||
|
this.dropoffLocationName,
|
||||||
|
this.pickupLatitude,
|
||||||
|
this.pickupLongitude,
|
||||||
|
this.dropoffLatitude,
|
||||||
|
this.dropoffLongitude,
|
||||||
|
this.appointmentNo,
|
||||||
|
this.appointmentClinicName,
|
||||||
|
this.appointmentDoctorName,
|
||||||
|
this.appointmentBranch,
|
||||||
|
this.appointmentTime});
|
||||||
|
|
||||||
|
TransportationDetails.fromJson(Map<String, dynamic> json) {
|
||||||
|
direction = json['direction'];
|
||||||
|
haveAppointment = json['haveAppointment'];
|
||||||
|
tripType = json['tripType'];
|
||||||
|
pickupUrgency = json['pickupUrgency'];
|
||||||
|
pickupSpot = json['pickupSpot'];
|
||||||
|
pickupDateTime = json['pickupDateTime'];
|
||||||
|
transportationType = json['transportationType'];
|
||||||
|
ambulate = json['ambulate'];
|
||||||
|
ambulateTitle = "";
|
||||||
|
notes = json['notes'];
|
||||||
|
requesterFileNo = json['requesterFileNo'];
|
||||||
|
requesterMobileNo = json['requesterMobileNo'];
|
||||||
|
requesterIsOutSA = json['requesterIsOutSA'];
|
||||||
|
pickupLocationName = json['pickupLocationName'];
|
||||||
|
dropoffLocationName = json['dropoffLocationName'];
|
||||||
|
pickupLatitude = json['pickup_Latitude'];
|
||||||
|
pickupLongitude = json['pickup_Longitude'];
|
||||||
|
dropoffLatitude = json['dropoff_Latitude'];
|
||||||
|
dropoffLongitude = json['dropoff_Longitude'];
|
||||||
|
appointmentNo = json['appointmentNo'];
|
||||||
|
appointmentClinicName = json['appointmentClinicName'];
|
||||||
|
appointmentDoctorName = json['appointmentDoctorName'];
|
||||||
|
appointmentBranch = json['appointmentBranch'];
|
||||||
|
appointmentTime = json['appointmentTime'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['direction'] = this.direction;
|
||||||
|
data['haveAppointment'] = this.haveAppointment;
|
||||||
|
data['tripType'] = this.tripType;
|
||||||
|
data['pickupUrgency'] = this.pickupUrgency;
|
||||||
|
data['pickupSpot'] = this.pickupSpot;
|
||||||
|
data['pickupDateTime'] = this.pickupDateTime;
|
||||||
|
data['transportationType'] = this.transportationType;
|
||||||
|
data['ambulate'] = this.ambulate;
|
||||||
|
data['ambulateTitle'] = this.ambulateTitle;
|
||||||
|
data['notes'] = this.notes;
|
||||||
|
data['requesterFileNo'] = this.requesterFileNo;
|
||||||
|
data['requesterMobileNo'] = this.requesterMobileNo;
|
||||||
|
data['requesterIsOutSA'] = this.requesterIsOutSA;
|
||||||
|
data['pickupLocationName'] = this.pickupLocationName;
|
||||||
|
data['dropoffLocationName'] = this.dropoffLocationName;
|
||||||
|
data['pickup_Latitude'] = this.pickupLatitude;
|
||||||
|
data['pickup_Longitude'] = this.pickupLongitude;
|
||||||
|
data['dropoff_Latitude'] = this.dropoffLatitude;
|
||||||
|
data['dropoff_Longitude'] = this.dropoffLongitude;
|
||||||
|
data['appointmentNo'] = this.appointmentNo;
|
||||||
|
data['appointmentClinicName'] = this.appointmentClinicName;
|
||||||
|
data['appointmentDoctorName'] = this.appointmentDoctorName;
|
||||||
|
data['appointmentBranch'] = this.appointmentBranch;
|
||||||
|
data['appointmentTime'] = this.appointmentTime;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
class PatientERTransportationMethod {
|
||||||
|
int? iD;
|
||||||
|
String? serviceID;
|
||||||
|
int? orderServiceID;
|
||||||
|
String? text;
|
||||||
|
String? textN;
|
||||||
|
dynamic price;
|
||||||
|
dynamic priceVAT;
|
||||||
|
dynamic priceTotal;
|
||||||
|
bool? isEnabled;
|
||||||
|
int? orderId;
|
||||||
|
int? quantity;
|
||||||
|
|
||||||
|
PatientERTransportationMethod(
|
||||||
|
{this.iD,
|
||||||
|
this.serviceID,
|
||||||
|
this.orderServiceID,
|
||||||
|
this.text,
|
||||||
|
this.textN,
|
||||||
|
this.price,
|
||||||
|
this.priceVAT,
|
||||||
|
this.priceTotal,
|
||||||
|
this.isEnabled,
|
||||||
|
this.orderId,
|
||||||
|
this.quantity});
|
||||||
|
|
||||||
|
PatientERTransportationMethod.fromJson(Map<String, dynamic> json) {
|
||||||
|
iD = json['ID'];
|
||||||
|
serviceID = json['ServiceID'];
|
||||||
|
orderServiceID = json['OrderServiceID'];
|
||||||
|
text = json['Text'];
|
||||||
|
textN = json['TextN'];
|
||||||
|
price = json['Price'];
|
||||||
|
priceVAT = json['PriceVAT'];
|
||||||
|
priceTotal = json['PriceTotal'];
|
||||||
|
isEnabled = json['IsEnabled'];
|
||||||
|
orderId = json['OrderId'];
|
||||||
|
quantity = json['Quantity'];
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||||
|
data['ID'] = this.iD;
|
||||||
|
data['ServiceID'] = this.serviceID;
|
||||||
|
data['OrderServiceID'] = this.orderServiceID;
|
||||||
|
data['Text'] = this.text;
|
||||||
|
data['TextN'] = this.textN;
|
||||||
|
data['Price'] = this.price;
|
||||||
|
data['PriceVAT'] = this.priceVAT;
|
||||||
|
data['PriceTotal'] = this.priceTotal;
|
||||||
|
data['IsEnabled'] = this.isEnabled;
|
||||||
|
data['OrderId'] = this.orderId;
|
||||||
|
data['Quantity'] = this.quantity;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
class GeocodeResponse {
|
||||||
|
final List<GeocodeResult> results;
|
||||||
|
final String status;
|
||||||
|
|
||||||
|
GeocodeResponse({
|
||||||
|
required this.results,
|
||||||
|
required this.status,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory GeocodeResponse.fromJson(Map<String, dynamic> json) {
|
||||||
|
final resultsList = (json['results'] as List<dynamic>? ?? [])
|
||||||
|
.map((e) => GeocodeResult.fromJson(e as Map<String, dynamic>))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
return GeocodeResponse(
|
||||||
|
results: resultsList,
|
||||||
|
status: json['status'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'GeocodeResponse(status: $status, results: [${results.map((r) => r.toString()).join(', ')}])';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class GeocodeResult {
|
||||||
|
final String formattedAddress;
|
||||||
|
final Geometry geometry;
|
||||||
|
final String placeId;
|
||||||
|
|
||||||
|
GeocodeResult({
|
||||||
|
required this.formattedAddress,
|
||||||
|
required this.geometry,
|
||||||
|
required this.placeId,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory GeocodeResult.fromJson(Map<String, dynamic> json) {
|
||||||
|
return GeocodeResult(
|
||||||
|
formattedAddress: json['formatted_address'] ?? '',
|
||||||
|
geometry: Geometry.fromJson(json['geometry']),
|
||||||
|
placeId: json['place_id'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'GeocodeResult(formattedAddress: $formattedAddress, placeId: $placeId, geometry: ${geometry.toString()})';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Geometry {
|
||||||
|
final Location location;
|
||||||
|
|
||||||
|
Geometry({required this.location});
|
||||||
|
|
||||||
|
factory Geometry.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Geometry(
|
||||||
|
location: Location.fromJson(json['location']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'Geometry(location: ${location.toString()})';
|
||||||
|
}
|
||||||
|
|
||||||
|
class Location {
|
||||||
|
final double lat;
|
||||||
|
final double lng;
|
||||||
|
|
||||||
|
Location({required this.lat, required this.lng});
|
||||||
|
|
||||||
|
factory Location.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Location(
|
||||||
|
lat: (json['lat'] as num).toDouble(),
|
||||||
|
lng: (json['lng'] as num).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'Location(lat: $lat, lng: $lng)';
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
class PlaceDetails {
|
||||||
|
final double lat;
|
||||||
|
final double lng;
|
||||||
|
|
||||||
|
PlaceDetails({required this.lat, required this.lng});
|
||||||
|
|
||||||
|
factory PlaceDetails.fromJson(Map<String, dynamic> json) {
|
||||||
|
final loc = json['result']['geometry']['location'];
|
||||||
|
return PlaceDetails(
|
||||||
|
lat: (loc['lat'] as num).toDouble(),
|
||||||
|
lng: (loc['lng'] as num).toDouble(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
class PlacePrediction {
|
||||||
|
final String description;
|
||||||
|
final String placeID;
|
||||||
|
PlacePrediction({required this.description, required this.placeID});
|
||||||
|
factory PlacePrediction.fromJson(Map<String, dynamic> json) {
|
||||||
|
return PlacePrediction(
|
||||||
|
description: json['description'] ?? '',
|
||||||
|
placeID: json['place_id'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,153 @@
|
|||||||
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/api/api_client.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/location/GeocodeResponse.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
|
||||||
|
|
||||||
|
import '../../core/api_consts.dart';
|
||||||
|
import 'PlacePrediction.dart';
|
||||||
|
|
||||||
|
abstract class LocationRepo {
|
||||||
|
Future<Either<Failure, GenericApiModel<List<PlacePrediction>>>>
|
||||||
|
getPlacePredictionsAsInput(String input);
|
||||||
|
|
||||||
|
Future<Either<Failure, GenericApiModel<PlaceDetails>>>
|
||||||
|
getPlaceDetailsOfSelectedPrediction(String placeId);
|
||||||
|
|
||||||
|
Future<Either<Failure, GenericApiModel<GeocodeResponse>>>
|
||||||
|
getGeoCodeFromLatLng(double lat, double lng);
|
||||||
|
}
|
||||||
|
|
||||||
|
class LocationRepoImpl implements LocationRepo {
|
||||||
|
final ApiClient apiClient;
|
||||||
|
|
||||||
|
LocationRepoImpl({required this.apiClient});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Either<Failure, GenericApiModel<List<PlacePrediction>>>> getPlacePredictionsAsInput(
|
||||||
|
String input) async {
|
||||||
|
final url = Uri.parse(
|
||||||
|
'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$input®ion=SA&key=$GOOGLE_API_KEY',
|
||||||
|
);
|
||||||
|
|
||||||
|
GenericApiModel<List<PlacePrediction>>? apiResponse;
|
||||||
|
Failure? failure;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apiClient.get(
|
||||||
|
url.toString(),
|
||||||
|
isExternal: true,
|
||||||
|
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||||
|
failure = failureType;
|
||||||
|
},
|
||||||
|
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||||
|
try {
|
||||||
|
final list = response['predictions'];
|
||||||
|
final predictionsList = list
|
||||||
|
.map((item) =>
|
||||||
|
PlacePrediction.fromJson(item as Map<String, dynamic>))
|
||||||
|
.toList()
|
||||||
|
.cast<PlacePrediction>();
|
||||||
|
apiResponse = GenericApiModel<List<PlacePrediction>>(
|
||||||
|
messageStatus: messageStatus,
|
||||||
|
statusCode: statusCode,
|
||||||
|
errorMessage: null,
|
||||||
|
data: predictionsList,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
failure = DataParsingFailure(e.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (failure != null) return Left(failure!);
|
||||||
|
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||||
|
return Right(apiResponse!);
|
||||||
|
} catch (e) {
|
||||||
|
return Left(UnknownFailure(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Either<Failure, GenericApiModel<GeocodeResponse>>> getGeoCodeFromLatLng(double lat, double lng) async {
|
||||||
|
final url = Uri.parse(
|
||||||
|
'https://maps.googleapis.com/maps/api/geocode/json?latlng=$lat,$lng&key=$GOOGLE_API_KEY',
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
GenericApiModel<GeocodeResponse>? apiResponse;
|
||||||
|
Failure? failure;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apiClient.get(
|
||||||
|
url.toString(),
|
||||||
|
isExternal: true,
|
||||||
|
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||||
|
failure = failureType;
|
||||||
|
},
|
||||||
|
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||||
|
try {
|
||||||
|
final predictionsList = GeocodeResponse.fromJson(response);
|
||||||
|
apiResponse = GenericApiModel<GeocodeResponse>(
|
||||||
|
messageStatus: messageStatus,
|
||||||
|
statusCode: statusCode,
|
||||||
|
errorMessage: null,
|
||||||
|
data: predictionsList,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
failure = DataParsingFailure(e.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (failure != null) return Left(failure!);
|
||||||
|
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||||
|
return Right(apiResponse!);
|
||||||
|
} catch (e) {
|
||||||
|
return Left(UnknownFailure(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Either<Failure, GenericApiModel<PlaceDetails>>> getPlaceDetailsOfSelectedPrediction(String placeId) async {
|
||||||
|
|
||||||
|
|
||||||
|
final url = Uri.parse(
|
||||||
|
'https://maps.googleapis.com/maps/api/place/details/json'
|
||||||
|
'?place_id=$placeId&fields=geometry&key=$GOOGLE_API_KEY',
|
||||||
|
);
|
||||||
|
|
||||||
|
GenericApiModel<PlaceDetails>? apiResponse;
|
||||||
|
Failure? failure;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apiClient.get(
|
||||||
|
url.toString(),
|
||||||
|
isExternal: true,
|
||||||
|
onFailure: (error, statusCode, {messageStatus, failureType}) {
|
||||||
|
failure = failureType;
|
||||||
|
},
|
||||||
|
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
|
||||||
|
try {
|
||||||
|
final predictionsList = PlaceDetails.fromJson(response);
|
||||||
|
apiResponse = GenericApiModel<PlaceDetails>(
|
||||||
|
messageStatus: messageStatus,
|
||||||
|
statusCode: statusCode,
|
||||||
|
errorMessage: null,
|
||||||
|
data: predictionsList,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
failure = DataParsingFailure(e.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (failure != null) return Left(failure!);
|
||||||
|
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
|
||||||
|
return Right(apiResponse!);
|
||||||
|
} catch (e) {
|
||||||
|
return Left(UnknownFailure(e.toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,120 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart' show ChangeNotifier;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:google_maps_flutter_platform_interface/src/types/camera.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/location/PlaceDetails.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/location/location_repo.dart';
|
||||||
|
import 'package:hmg_patient_app_new/services/error_handler_service.dart';
|
||||||
|
import 'package:huawei_map/huawei_map.dart' as HMSCameraServices;
|
||||||
|
import 'package:google_maps_flutter/google_maps_flutter.dart' as GMSMapServices;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import 'PlacePrediction.dart';
|
||||||
|
|
||||||
|
class LocationViewModel extends ChangeNotifier {
|
||||||
|
final LocationRepo locationRepo;
|
||||||
|
final ErrorHandlerService errorHandlerService;
|
||||||
|
|
||||||
|
LocationViewModel({required this.locationRepo, required this.errorHandlerService});
|
||||||
|
|
||||||
|
List<PlacePrediction> predictions = [];
|
||||||
|
PlacePrediction? selectedPrediction;
|
||||||
|
bool isPredictionLoading = false;
|
||||||
|
GeocodeResponse? geocodeResponse;
|
||||||
|
PlaceDetails? placeDetails;
|
||||||
|
|
||||||
|
Location? mapCapturedLocation;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FutureOr<void> getPlacesPrediction(String input) async {
|
||||||
|
predictions = [];
|
||||||
|
isPredictionLoading= true;
|
||||||
|
final result = await locationRepo.getPlacePredictionsAsInput(input);
|
||||||
|
result.fold(
|
||||||
|
(failure) {
|
||||||
|
errorHandlerService.handleError(failure: failure);
|
||||||
|
},
|
||||||
|
(apiModel) {
|
||||||
|
predictions = apiModel.data??[];
|
||||||
|
},
|
||||||
|
);
|
||||||
|
isPredictionLoading = false;
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> getPlaceEncodedData(double? lat, double? lng) async {
|
||||||
|
geocodeResponse = null;
|
||||||
|
final result = await locationRepo.getGeoCodeFromLatLng(lat!, lng!);
|
||||||
|
result.fold(
|
||||||
|
(failure) {
|
||||||
|
errorHandlerService.handleError(failure: failure);
|
||||||
|
},
|
||||||
|
(apiModel) {
|
||||||
|
print("Geocode Response: ${apiModel.data}");
|
||||||
|
geocodeResponse = apiModel.data;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> getPlaceDetails(String placeID) async {
|
||||||
|
placeDetails = null;
|
||||||
|
final result = await locationRepo.getPlaceDetailsOfSelectedPrediction(placeID);
|
||||||
|
result.fold(
|
||||||
|
(failure) {
|
||||||
|
errorHandlerService.handleError(failure: failure);
|
||||||
|
},
|
||||||
|
(apiModel) {
|
||||||
|
placeDetails = apiModel.data;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleGMSMapCameraMoved(GMSMapServices.CameraPosition value) {
|
||||||
|
mapCapturedLocation = Location(lat: value.target.latitude, lng: value.target.longitude);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHMSMapCameraMoved(HMSCameraServices.CameraPosition value) {
|
||||||
|
mapCapturedLocation = Location(lat: value.target.lat, lng: value.target.lng);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOnCameraIdle(){
|
||||||
|
if(mapCapturedLocation != null) {
|
||||||
|
getPlaceEncodedData(mapCapturedLocation!.lat, mapCapturedLocation!.lng);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSearchQuery(String? value) {
|
||||||
|
if(value == null || value.isEmpty){
|
||||||
|
predictions = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlacesPrediction(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flushSearchPredictions() {
|
||||||
|
predictions = [];
|
||||||
|
mapCapturedLocation= null;
|
||||||
|
placeDetails= null;
|
||||||
|
geocodeResponse= null;
|
||||||
|
selectedPrediction= null;
|
||||||
|
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
FutureOr<void> selectPlacePrediction(PlacePrediction placePrediction) async{
|
||||||
|
selectedPrediction= placePrediction;
|
||||||
|
await getPlaceDetails(placePrediction.placeID);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
// import 'package:flutter/cupertino.dart';
|
||||||
|
// import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
|
||||||
|
// import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
// import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
|
||||||
|
// import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart';
|
||||||
|
// import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointment_card.dart';
|
||||||
|
// import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/doctor_card.dart';
|
||||||
|
// import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
// import 'package:provider/provider.dart';
|
||||||
|
//
|
||||||
|
// class AppointmentBottomSheet extends StatelessWidget{
|
||||||
|
// @override
|
||||||
|
// Widget build(BuildContext context) {
|
||||||
|
// // TODO: implement build
|
||||||
|
// throw UnimplementedError();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Widget getAppointList(EmergencyServicesViewModel viewmodel, List<PatientAppointmentHistoryResponseModel> filteredAppointmentList) {
|
||||||
|
// return Column(
|
||||||
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
// children: [
|
||||||
|
// ListView.separated(
|
||||||
|
// padding: EdgeInsets.only(top: 24.h),
|
||||||
|
// shrinkWrap: true,
|
||||||
|
// physics: NeverScrollableScrollPhysics(),
|
||||||
|
// itemCount: viewmodel.isMyAppointmentsLoading
|
||||||
|
// ? 5
|
||||||
|
// : filteredAppointmentList.isNotEmpty
|
||||||
|
// ? filteredAppointmentList.length
|
||||||
|
// : 1,
|
||||||
|
// itemBuilder: (context, index) {
|
||||||
|
// return viewmodel.isMyAppointmentsLoading
|
||||||
|
// ? Container(
|
||||||
|
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
|
||||||
|
// child: DoctorCard(doctorsListResponseModel: doctorsListResponseModel, isLoading: viewmodel.isMyAppointmentsLoading, bookAppointmentsViewModel: bookAppointmentsViewModel),
|
||||||
|
// ).paddingSymmetrical(24.h, 0.h)
|
||||||
|
// : filteredAppointmentList.isNotEmpty
|
||||||
|
// ? AnimationConfiguration.staggeredList(
|
||||||
|
// position: index,
|
||||||
|
// duration: const Duration(milliseconds: 500),
|
||||||
|
// child: SlideAnimation(
|
||||||
|
// verticalOffset: 100.0,
|
||||||
|
// child: FadeInAnimation(
|
||||||
|
// child: AnimatedContainer(
|
||||||
|
// duration: Duration(milliseconds: 300),
|
||||||
|
// curve: Curves.easeInOut,
|
||||||
|
// decoration: RoundedRectangleBorder().toSmoothCornerDecoration(color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: true),
|
||||||
|
// child: AppointmentCard(
|
||||||
|
// patientAppointmentHistoryResponseModel: filteredAppointmentList[index],
|
||||||
|
// myAppointmentsViewModel: myAppointmentsViewModel,
|
||||||
|
// bookAppointmentsViewModel: bookAppointmentsViewModel,
|
||||||
|
// isLoading: false,
|
||||||
|
// isFromHomePage: false,
|
||||||
|
// ),
|
||||||
|
// ).paddingSymmetrical(24.h, 0.h),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// )
|
||||||
|
// : Utils.getNoDataWidget(
|
||||||
|
// context,
|
||||||
|
// noDataText: "You don't have any appointments yet.".needTranslation,
|
||||||
|
// callToActionButton: CustomButton(
|
||||||
|
// text: LocaleKeys.bookAppo.tr(context: context),
|
||||||
|
// onPressed: () {
|
||||||
|
// Navigator.of(context).push(
|
||||||
|
// CustomPageRoute(
|
||||||
|
// page: BookAppointmentPage(),
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// backgroundColor: Color(0xffFEE9EA),
|
||||||
|
// borderColor: Color(0xffFEE9EA),
|
||||||
|
// textColor: Color(0xffED1C2B),
|
||||||
|
// fontSize: 14.f,
|
||||||
|
// fontWeight: FontWeight.w500,
|
||||||
|
// borderRadius: 12.r,
|
||||||
|
// padding: EdgeInsets.fromLTRB(10.w, 0, 10.w, 0),
|
||||||
|
// height: 40.h,
|
||||||
|
// icon: AppAssets.add_icon,
|
||||||
|
// iconColor: AppColors.primaryRedColor,
|
||||||
|
// ).paddingSymmetrical(48.h, 0.h),
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// separatorBuilder: (BuildContext cxt, int index) => SizedBox(height: 16.h),
|
||||||
|
// ),
|
||||||
|
// SizedBox(height: 24.h),
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// }
|
||||||
@ -0,0 +1,172 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.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/emergency_services/emergency_services_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/emergency_services/models/ambulance_direction.dart';
|
||||||
|
import 'package:hmg_patient_app_new/theme/colors.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import '../../../../generated/locale_keys.g.dart' show LocaleKeys;
|
||||||
|
|
||||||
|
class PickupLocation extends StatelessWidget {
|
||||||
|
final VoidCallback onTap;
|
||||||
|
const PickupLocation({super.key, required this.onTap});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
"Select Pickup Direction"
|
||||||
|
.needTranslation
|
||||||
|
.toText24(color: AppColors.textColor,isBold: true),
|
||||||
|
SizedBox(
|
||||||
|
height: 16.h,
|
||||||
|
),
|
||||||
|
"Select Direction"
|
||||||
|
.needTranslation
|
||||||
|
.toText14(color: AppColors.textColor, weight: FontWeight.w600),
|
||||||
|
SizedBox(
|
||||||
|
height: 12.h,
|
||||||
|
),
|
||||||
|
Selector<EmergencyServicesViewModel, AmbulanceCallingPlace>(
|
||||||
|
selector: (context, viewModel) => viewModel.callingPlace,
|
||||||
|
builder: (context, value, _) {
|
||||||
|
return Column(
|
||||||
|
spacing: 12.h,
|
||||||
|
children: [
|
||||||
|
RadioGroup<AmbulanceCallingPlace>(
|
||||||
|
groupValue: value,
|
||||||
|
onChanged: (value) {
|
||||||
|
context
|
||||||
|
.read<EmergencyServicesViewModel>()
|
||||||
|
.updateCallingPlace(value);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
spacing: 24.h,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: AmbulanceCallingPlace.TO_HOSPITAL,
|
||||||
|
groupValue: value,
|
||||||
|
activeColor: AppColors.primaryRedColor,
|
||||||
|
|
||||||
|
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
|
||||||
|
),
|
||||||
|
"To Hospital"
|
||||||
|
.needTranslation
|
||||||
|
.toText12(color: AppColors.textColor)
|
||||||
|
],
|
||||||
|
).onPress((){
|
||||||
|
context
|
||||||
|
.read<EmergencyServicesViewModel>()
|
||||||
|
.updateCallingPlace(AmbulanceCallingPlace.TO_HOSPITAL);
|
||||||
|
}),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: AmbulanceCallingPlace.FROM_HOSPITAL,
|
||||||
|
activeColor: AppColors.primaryRedColor,
|
||||||
|
|
||||||
|
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
|
||||||
|
),
|
||||||
|
"From Hospital"
|
||||||
|
.needTranslation
|
||||||
|
.toText12(color: AppColors.textColor)
|
||||||
|
],
|
||||||
|
).onPress((){
|
||||||
|
context
|
||||||
|
.read<EmergencyServicesViewModel>()
|
||||||
|
.updateCallingPlace(AmbulanceCallingPlace.FROM_HOSPITAL);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: value == AmbulanceCallingPlace.TO_HOSPITAL,
|
||||||
|
child: Selector<EmergencyServicesViewModel, AmbulanceDirection>(
|
||||||
|
selector: (context, viewModel) =>
|
||||||
|
viewModel.ambulanceDirection,
|
||||||
|
builder: (context, directionValue, _) {
|
||||||
|
return Column(
|
||||||
|
spacing: 12.h,
|
||||||
|
children: [
|
||||||
|
"Select Way"
|
||||||
|
.needTranslation
|
||||||
|
.toText14(color: AppColors.textColor, weight: FontWeight.w600),
|
||||||
|
RadioGroup<AmbulanceDirection>(
|
||||||
|
groupValue: directionValue,
|
||||||
|
onChanged: (value) {
|
||||||
|
context
|
||||||
|
.read<EmergencyServicesViewModel>()
|
||||||
|
.updateDirection(value);
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
spacing: 24.h,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: AmbulanceDirection.ONE_WAY,
|
||||||
|
activeColor: AppColors.primaryRedColor,
|
||||||
|
|
||||||
|
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
|
||||||
|
),
|
||||||
|
"One Way"
|
||||||
|
.needTranslation
|
||||||
|
.toText12(color: AppColors.textColor)
|
||||||
|
],
|
||||||
|
).onPress((){
|
||||||
|
context
|
||||||
|
.read<EmergencyServicesViewModel>()
|
||||||
|
.updateDirection(AmbulanceDirection.ONE_WAY);
|
||||||
|
}),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Radio(
|
||||||
|
value: AmbulanceDirection.TWO_WAY,
|
||||||
|
// activeColor: AppColors.primaryRedColor,
|
||||||
|
|
||||||
|
fillColor: MaterialStateProperty.all(AppColors.primaryRedColor),
|
||||||
|
),
|
||||||
|
"Two Way"
|
||||||
|
.needTranslation
|
||||||
|
.toText12(color: AppColors.textColor)
|
||||||
|
],
|
||||||
|
).onPress((){
|
||||||
|
context
|
||||||
|
.read<EmergencyServicesViewModel>()
|
||||||
|
.updateDirection(AmbulanceDirection.TWO_WAY);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
SizedBox(
|
||||||
|
height: 16.h,
|
||||||
|
),
|
||||||
|
CustomButton(
|
||||||
|
text: LocaleKeys.confirm.tr(context: context),
|
||||||
|
onPressed: onTap,
|
||||||
|
backgroundColor: AppColors.primaryRedColor,
|
||||||
|
borderColor: AppColors.primaryRedColor,
|
||||||
|
textColor: AppColors.whiteColor,
|
||||||
|
iconColor: AppColors.whiteColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'
|
||||||
|
show AnimationConfiguration, SlideAnimation, FadeInAnimation;
|
||||||
|
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/enums.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/debouncer.dart';
|
||||||
|
import 'package:hmg_patient_app_new/core/utils/utils.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/string_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/extensions/widget_extensions.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/location/GeocodeResponse.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/location/PlacePrediction.dart';
|
||||||
|
import 'package:hmg_patient_app_new/features/location/location_view_model.dart';
|
||||||
|
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
|
||||||
|
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
import '../../../theme/colors.dart';
|
||||||
|
import '../../appointments/widgets/hospital_bottom_sheet/type_selection_widget.dart';
|
||||||
|
|
||||||
|
class LocationInputBottomSheet extends StatelessWidget {
|
||||||
|
final Debouncer debouncer = Debouncer(milliseconds: 500);
|
||||||
|
|
||||||
|
LocationInputBottomSheet({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
TextInputWidget(
|
||||||
|
labelText: LocaleKeys.search.tr(),
|
||||||
|
hintText: "Search Location".needTranslation,
|
||||||
|
controller: TextEditingController(),
|
||||||
|
onChange: (value){
|
||||||
|
debouncer.run(() {
|
||||||
|
context.read<LocationViewModel>().updateSearchQuery(value);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isEnable: true,
|
||||||
|
prefix: null,
|
||||||
|
autoFocus: false,
|
||||||
|
isBorderAllowed: false,
|
||||||
|
keyboardType: TextInputType.text,
|
||||||
|
isAllowLeadingIcon: true,
|
||||||
|
selectionType: SelectionTypeEnum.search,
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
vertical: 10.h,
|
||||||
|
horizontal: 15.h,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 24.h),
|
||||||
|
Selector<LocationViewModel, List<PlacePrediction>>(
|
||||||
|
selector: (_, vm) => vm.predictions ?? <PlacePrediction>[],
|
||||||
|
builder: (context, predictions, _) {
|
||||||
|
if (predictions.isEmpty) return SizedBox.shrink();
|
||||||
|
return Expanded(
|
||||||
|
child: ListView.separated(
|
||||||
|
separatorBuilder: (_,__)=>SizedBox(height: 16.h,),
|
||||||
|
itemCount: predictions.length,
|
||||||
|
itemBuilder: (_, index) {
|
||||||
|
final prediction = predictions[index];
|
||||||
|
|
||||||
|
return AnimationConfiguration.staggeredList(
|
||||||
|
position: index,
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
child: SlideAnimation(
|
||||||
|
verticalOffset: 100.0,
|
||||||
|
child: FadeInAnimation(
|
||||||
|
child: AnimatedContainer(
|
||||||
|
duration: Duration(milliseconds: 300),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
decoration: RoundedRectangleBorder()
|
||||||
|
.toSmoothCornerDecoration(
|
||||||
|
color: AppColors.whiteColor,
|
||||||
|
borderRadius: 24.h,
|
||||||
|
hasShadow: true),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: prediction.description.toText14(
|
||||||
|
color: AppColors.textColor,
|
||||||
|
weight: FontWeight.w500),
|
||||||
|
),
|
||||||
|
|
||||||
|
Utils.buildSvgWithAssets(
|
||||||
|
icon: AppAssets.forward_arrow_icon_small,
|
||||||
|
iconColor: AppColors.blackColor,
|
||||||
|
width: 20.h,
|
||||||
|
height: 15.h,
|
||||||
|
fit: BoxFit.contain,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
).paddingAll(24.h),),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).onPress(() async {
|
||||||
|
await context.read<LocationViewModel>().selectPlacePrediction(prediction);
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
var location = context.read<LocationViewModel>().placeDetails;
|
||||||
|
if(location != null) {
|
||||||
|
context.read<EmergencyServicesViewModel>().moveController(
|
||||||
|
Location(lat: location.lat, lng: location.lng));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,41 +1,63 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.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/utils/utils.dart';
|
||||||
import 'package:huawei_map/huawei_map.dart' ;
|
import 'package:huawei_map/huawei_map.dart' ;
|
||||||
|
|
||||||
class HMSMap extends StatefulWidget{
|
class HMSMap extends StatefulWidget{
|
||||||
final CameraPosition currentLocation;
|
final CameraPosition currentLocation;
|
||||||
final Function(CameraPosition) onCameraMoved;
|
final Function(CameraPosition) onCameraMoved;
|
||||||
|
final VoidCallback onCameraIdle;
|
||||||
final MapType mapType;
|
final MapType mapType;
|
||||||
final bool compassEnabled;
|
final bool compassEnabled;
|
||||||
final bool myLocationEnabled;
|
final bool myLocationEnabled;
|
||||||
|
final bool showCenterMarker;
|
||||||
|
final Completer<HuaweiMapController>? inputController;
|
||||||
|
|
||||||
HMSMap({super.key, required this.currentLocation ,required this.onCameraMoved, this.mapType = MapType.normal,this.compassEnabled = false, this.myLocationEnabled = true});
|
HMSMap({super.key, required this.currentLocation ,required this.onCameraMoved,required this.onCameraIdle,
|
||||||
|
this.mapType = MapType.normal,this.compassEnabled = false,this.showCenterMarker = false,
|
||||||
|
this.myLocationEnabled = true, this.inputController});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<HMSMap> createState() => _HMSMapState();
|
State<HMSMap> createState() => _HMSMapState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HMSMapState extends State<HMSMap> {
|
class _HMSMapState extends State<HMSMap> {
|
||||||
final Completer<HuaweiMapController> _controller = Completer<HuaweiMapController>();
|
Completer<HuaweiMapController>? controller;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
HuaweiMapInitializer.initializeMap();
|
HuaweiMapInitializer.initializeMap();
|
||||||
|
controller = widget.inputController ?? Completer<HuaweiMapController>();
|
||||||
super.initState();
|
super.initState();
|
||||||
}
|
}
|
||||||
// @override
|
// @override
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) =>
|
Widget build(BuildContext context) =>
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
HuaweiMap(
|
HuaweiMap(
|
||||||
mapType: widget.mapType,
|
mapType: widget.mapType,
|
||||||
zoomControlsEnabled: false,
|
zoomControlsEnabled: false,
|
||||||
myLocationEnabled: widget.myLocationEnabled,
|
myLocationEnabled: widget.myLocationEnabled,
|
||||||
|
myLocationButtonEnabled: false,
|
||||||
compassEnabled: widget.compassEnabled,
|
compassEnabled: widget.compassEnabled,
|
||||||
|
onCameraIdle:()=> widget.onCameraIdle(),
|
||||||
initialCameraPosition: widget.currentLocation,
|
initialCameraPosition: widget.currentLocation,
|
||||||
onCameraMove: (value) => widget.onCameraMoved(value),
|
onCameraMove: (value) => widget.onCameraMoved(value),
|
||||||
onMapCreated: (HuaweiMapController controller) {
|
onMapCreated: (HuaweiMapController controller) {
|
||||||
_controller.complete(controller);
|
this.controller?.complete(controller);
|
||||||
},
|
},
|
||||||
|
),
|
||||||
|
Visibility(
|
||||||
|
visible: widget.showCenterMarker,
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
child: Utils.buildSvgWithAssets(icon: AppAssets.pin_location, width: 24.w, height: 36.h),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue