nearest er clinic repo viewmodel and view added

pull/84/head
tahaalam 3 weeks ago
parent 97959f7f28
commit c146747e5e

@ -0,0 +1,3 @@
<svg width="14" height="15" viewBox="0 0 14 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.99996 1.22925C3.53667 1.22925 0.729126 4.0368 0.729126 7.50008C0.729126 10.9634 3.53667 13.7709 6.99996 13.7709C10.4632 13.7709 13.2708 10.9634 13.2708 7.50008C13.2708 4.0368 10.4632 1.22925 6.99996 1.22925ZM9.74577 5.57923C9.97358 5.35142 9.97358 4.98207 9.74577 4.75427C9.51797 4.52646 9.14862 4.52646 8.92081 4.75427L7 6.67509L5.95409 5.62925C5.72628 5.40146 5.35693 5.40147 5.12913 5.62928C4.90133 5.8571 4.90135 6.22644 5.12916 6.45424L6.17504 7.50004L6.00415 7.67094C5.77634 7.89874 5.77634 8.26809 6.00415 8.49589C6.23195 8.7237 6.6013 8.7237 6.8291 8.49589L7.00002 8.32497L7.17076 8.49569C7.39857 8.72349 7.76792 8.72348 7.99571 8.49567C8.22351 8.26785 8.2235 7.89851 7.99569 7.67071L7.82498 7.50002L9.74577 5.57923Z" fill="#2E3039"/>
</svg>

After

Width:  |  Height:  |  Size: 902 B

@ -158,6 +158,7 @@ class AppAssets {
static const String call_ambulance_icon = '$svgBasePath/call_ambulance_icon.svg'; static const String call_ambulance_icon = '$svgBasePath/call_ambulance_icon.svg';
static const String nearest_er_icon = '$svgBasePath/nearest_er_icon.svg'; static const String nearest_er_icon = '$svgBasePath/nearest_er_icon.svg';
static const String rrt_icon = '$svgBasePath/rrt_icon.svg'; static const String rrt_icon = '$svgBasePath/rrt_icon.svg';
static const String waiting_time_clock = '$svgBasePath/waiting_time_clock.svg';
//bottom navigation// //bottom navigation//
static const String homeBottom = '$svgBasePath/home_bottom.svg'; static const String homeBottom = '$svgBasePath/home_bottom.svg';

@ -9,6 +9,8 @@ import 'package:hmg_patient_app_new/features/book_appointments/book_appointments
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/common/common_repo.dart'; import 'package:hmg_patient_app_new/features/common/common_repo.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_repo.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_repo.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_repo.dart';
@ -38,6 +40,7 @@ import 'package:hmg_patient_app_new/services/localauth_service.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart'; import 'package:hmg_patient_app_new/services/logger_service.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart'; import 'package:hmg_patient_app_new/widgets/date_range_selector/viewmodel/date_range_view_model.dart';
import 'package:http/http.dart';
import 'package:local_auth/local_auth.dart'; import 'package:local_auth/local_auth.dart';
import 'package:logger/web.dart'; import 'package:logger/web.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -99,6 +102,9 @@ class AppDependencies {
getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<HabibWalletRepo>(() => HabibWalletRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<MedicalFileRepo>(() => MedicalFileRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<ImmediateLiveCareRepo>(() => ImmediateLiveCareRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt())); getIt.registerLazySingleton<ImmediateLiveCareRepo>(() => ImmediateLiveCareRepoImp(loggerService: getIt<LoggerService>(), apiClient: getIt()));
getIt.registerLazySingleton<EmergencyServicesRepo>(() =>
EmergencyServicesRepoImpl(
loggerService: getIt<LoggerService>(), apiClient: getIt()));
// ViewModels // ViewModels
// Global/shared VMs LazySingleton // Global/shared VMs LazySingleton
@ -186,17 +192,13 @@ class AppDependencies {
() => DoctorFilterViewModel(), () => DoctorFilterViewModel(),
); );
getIt.registerLazySingleton<AppointmentViaRegionViewmodel>( getIt.registerLazySingleton<EmergencyServicesViewModel>(
() => AppointmentViaRegionViewmodel(navigationService: getIt(), appState: getIt()), () => EmergencyServicesViewModel(
locationUtils: getIt(),
navServices: getIt(),
viewModel: getIt(),
appState: getIt(),
),
); );
// Screen-specific VMs Factory
// getIt.registerFactory<BookAppointmentsViewModel>(
// () => BookAppointmentsViewModel(
// bookAppointmentsRepo: getIt(),
// dialogService: getIt(),
// errorHandlerService: getIt(),
// ),
// );
} }
} }

@ -36,7 +36,8 @@ class LocationUtils {
isGMSDevice = GmsCheck().checkGmsAvailability(); isGMSDevice = GmsCheck().checkGmsAvailability();
} }
void getLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure}) async { void getLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure,bool isShowConfirmDialog = false}) async {
this.isShowConfirmDialog = isShowConfirmDialog;
if (Platform.isIOS) { if (Platform.isIOS) {
getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess); getCurrentLocation(onFailure: onFailure, onSuccess: onSuccess);
return; return;
@ -50,7 +51,7 @@ class LocationUtils {
getHMSLocation(onFailure: onFailure, onSuccess: onSuccess); getHMSLocation(onFailure: onFailure, onSuccess: onSuccess);
} }
void getCurrentLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure}) async { void getCurrentLocation({Function(LatLng)? onSuccess, VoidCallback? onFailure, }) async {
var location = Location(); var location = Location();
bool isLocationEnabled = await location.serviceEnabled(); bool isLocationEnabled = await location.serviceEnabled();
@ -73,12 +74,14 @@ class LocationUtils {
return; return;
} }
} else if (permissionGranted == LocationPermission.deniedForever) { } else if (permissionGranted == LocationPermission.deniedForever) {
if (isShowConfirmDialog) { if (onFailure == null && isShowConfirmDialog) {
showCommonBottomSheetWithoutHeight( showCommonBottomSheetWithoutHeight(
title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!), title: LocaleKeys.notice.tr(context: navigationService.navigatorKey.currentContext!),
navigationService.navigatorKey.currentContext!, navigationService.navigatorKey.currentContext!,
child: Utils.getWarningWidget( child: Utils.getWarningWidget(
loadingText: "Please grant location permission from app settings to see the nearest ER location details".needTranslation, loadingText:
"Please grant location permission from app settings to see better results"
.needTranslation,
isShowActionButtons: true, isShowActionButtons: true,
onCancelTap: () { onCancelTap: () {
navigationService.pop(); navigationService.pop();
@ -91,7 +94,10 @@ class LocationUtils {
isFullScreen: false, isFullScreen: false,
isCloseButtonVisible: true, isCloseButtonVisible: true,
); );
return;
} }
onFailure?.call();
return;
} }
Position? currentLocation = await Geolocator.getLastKnownPosition(); Position? currentLocation = await Geolocator.getLastKnownPosition();

@ -0,0 +1,19 @@
import 'dart:async';
import 'dart:ui';
class Debouncer {
final int milliseconds;
VoidCallback? action;
Timer? _timer;
Debouncer({required this.milliseconds});
void run(VoidCallback action) {
_timer?.cancel();
_timer = Timer(Duration(milliseconds: milliseconds), action);
}
void dispose() {
_timer?.cancel();
}
}

@ -0,0 +1,56 @@
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/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/services/logger_service.dart';
import '../../core/api_consts.dart' show GET_NEAREST_HOSPITAL;
abstract interface class EmergencyServicesRepo {
Future<Either<Failure, GenericApiModel<List<ProjectAvgERWaitingTime>>>> getNearestEr({int? id, int? projectID});
}
class EmergencyServicesRepoImpl implements EmergencyServicesRepo {
final ApiClient apiClient;
final LoggerService loggerService;
EmergencyServicesRepoImpl({required this.loggerService, required this.apiClient});
@override
Future<Either<Failure, GenericApiModel<List<ProjectAvgERWaitingTime>>>> getNearestEr({int? id, int? projectID}) async {
Map<String, dynamic> mapDevice = {'IsForER' : true};
try {
GenericApiModel<List<ProjectAvgERWaitingTime>>? apiResponse;
Failure? failure;
await apiClient.post(
GET_NEAREST_HOSPITAL,
body: mapDevice,
onFailure: (error, statusCode, {messageStatus, failureType}) {
failure = failureType;
},
onSuccess: (response, statusCode, {messageStatus, errorMessage}) {
try {
final list = response['List_ProjectAvgERWaitingTime'];
final clinicsList = list.map((item) => ProjectAvgERWaitingTime.fromJson(item as Map<String, dynamic>)).toList().cast<ProjectAvgERWaitingTime>();
apiResponse = GenericApiModel<List<ProjectAvgERWaitingTime>>(
messageStatus: messageStatus,
statusCode: statusCode,
errorMessage: null,
data: clinicsList,
);
} catch (e) {
failure = DataParsingFailure(e.toString());
}
},
);
if (failure != null) return Left(failure!);
if (apiResponse == null) return Left(ServerFailure("Unknown error"));
return Right(apiResponse!);
} catch (e) {
return Left(UnknownFailure(e.toString()));
}
}
}

@ -0,0 +1,103 @@
import 'dart:async' show FutureOr;
import 'package:flutter/foundation.dart' show ChangeNotifier;
import 'package:hmg_patient_app_new/core/app_state.dart';
import 'package:hmg_patient_app_new/core/location_util.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_repo.dart';
import 'package:hmg_patient_app_new/features/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart';
import 'package:hmg_patient_app_new/services/navigation_service.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:url_launcher/url_launcher.dart' show canLaunchUrl, launchUrl, LaunchMode;
class EmergencyServicesViewModel extends ChangeNotifier {
final NavigationService navServices;
final LocationUtils? locationUtils;
final EmergencyServicesRepo viewModel;
final AppState appState;
bool isERListLoading = false;
List<ProjectAvgERWaitingTime> nearestERList = [];
List<ProjectAvgERWaitingTime> nearestERFilteredList = [];
EmergencyServicesViewModel(
{required this.navServices,
required this.locationUtils,
required this.viewModel,
required this.appState});
void navigateToNearestERPage() {
locationUtils!.getLocation(
isShowConfirmDialog: true,
onSuccess: (position) {
navServices.push(
CustomPageRoute(
page: NearestErPage(),
),
);
getNearestER();
});
}
void filterErList(String query) {
print ("the query is $query");
if (query.isEmpty) {
nearestERFilteredList = nearestERList;
} else {
nearestERFilteredList = nearestERList
.where((er) =>
er.projectName != null &&
er.projectName!.toLowerCase().contains(query.toLowerCase()))
.toList();
}
notifyListeners();
}
// Open directions (navigation) from current location to destination.
Future<void> openDirections({
required double destLat,
required double destLng,
String? travelMode, // driving, walking, bicycling, transit
}) async {
// Try Google Maps app navigation intent (android/iOS)
final modeParam = travelMode == null ? 'driving' : travelMode;
final googleNavUri = Uri.parse('google.navigation:q=$destLat,$destLng&mode=${modeParam.substring(0,1)}'); // mode: d/w/b/t by scheme
final universalUrl = Uri.parse('https://www.google.com/maps/dir/?api=1&destination=$destLat,$destLng&travelmode=$modeParam');
if (await canLaunchUrl(googleNavUri)) {
await launchUrl(googleNavUri, mode: LaunchMode.externalApplication);
} else {
await launchUrl(universalUrl, mode: LaunchMode.externalApplication);
}
}
Future<void> openDialer(String phoneNumber) async {
final Uri telUri = Uri(scheme: 'tel', path: phoneNumber);
if (await canLaunchUrl(telUri)) {
await launchUrl(telUri, mode: LaunchMode.externalApplication);
} else {
throw 'Could not open dialer for $phoneNumber';
}
}
FutureOr<void> getNearestER() async {
isERListLoading = true;
nearestERList = [];
nearestERFilteredList = [];
notifyListeners();
var response = await viewModel.getNearestEr();
isERListLoading = false;
notifyListeners();
response.fold(
(failure) async {
},
(apiResponse) {
isERListLoading = false;
if (apiResponse.messageStatus == 1) {
nearestERList = apiResponse.data!;
nearestERFilteredList = nearestERList;
}
},
);
}
}

@ -0,0 +1,114 @@
class ProjectAvgERWaitingTime {
int? iD;
int? projectID;
int? avgTimeInMinutes;
String? avgTimeInHHMM;
dynamic distanceInKilometers;
String? latitude;
String? longitude;
String? phonenumber;
String? projectImageURL;
String? projectName;
ProjectAvgERWaitingTime(
{this.iD,
this.projectID,
this.avgTimeInMinutes,
this.avgTimeInHHMM,
this.distanceInKilometers,
this.latitude,
this.longitude,
this.phonenumber,
this.projectImageURL,
this.projectName});
ProjectAvgERWaitingTime.fromJson(Map<String, dynamic> json) {
iD = json['ID'];
projectID = json['ProjectID'];
avgTimeInMinutes = json['AvgTimeInMinutes'];
avgTimeInHHMM = json['AvgTimeInHHMM'];
distanceInKilometers = json['DistanceInKilometers'];
latitude = json['Latitude'];
longitude = json['Longitude'];
phonenumber = json['PhoneNumber'];
projectImageURL = json['ProjectImageURL'];
projectName = json['ProjectName'];
}
String getTime(){
print("the name is $projectName");
print("the avgTimeInMinutes is $avgTimeInMinutes");
if(avgTimeInMinutes == null) return "";
int hours = avgTimeInMinutes! ~/ 60;
int minutes = avgTimeInMinutes! % 60;
print("the time is ${"${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}"}");
return "${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}";
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['ID'] = this.iD;
data['ProjectID'] = this.projectID;
data['AvgTimeInMinutes'] = this.avgTimeInMinutes;
data['AvgTimeInHHMM'] = this.avgTimeInHHMM;
data['DistanceInKilometers'] = this.distanceInKilometers;
data['Latitude'] = this.latitude;
data['Longitude'] = this.longitude;
data['PhoneNumber'] = this.phonenumber;
data['ProjectImageURL'] = this.projectImageURL;
data['ProjectName'] = this.projectName;
return data;
}
}
//class ProjectAvgERWaitingTime {
// int? iD;
// int? projectID;
// int? avgTimeInMinutes;
// String? avgTimeInHHMM;
// String? distanceInKilometers;
// String? latitude;
// String? longitude;
// String? phonenum?ber;
// String? projectImageURL;
// String? projectName;
//
// ProjectAvgERWaitingTime(
// {this.iD,
// this.projectID,
// this.avgTimeInMinutes,
// this.avgTimeInHHMM,
// this.distanceInKilometers,
// this.latitude,
// this.longitude,
// this.phonenum?ber,
// this.projectImageURL,
// this.projectName});
//
// ProjectAvgERWaitingTime.fromJson(Map<String, dynamic> json) {
// iD = json['ID'];
// projectID = json['ProjectID'];
// avgTimeInMinutes = json['AvgTimeInMinutes'];
// avgTimeInHHMM = json['AvgTimeInHHMM'];
// distanceInKilometers = json['DistanceInKilometers'];
// latitude = json['Latitude'];
// longitude = json['Longitude'];
// phonenum?ber = json['Phonenum?ber'];
// projectImageURL = json['ProjectImageURL'];
// projectName = json['ProjectName'];
// }
//
// Map<String, dynamic> toJson() {
// final Map<String, dynamic> data = new Map<String, dynamic>();
// data['ID'] = this.iD;
// data['ProjectID'] = this.projectID;
// data['AvgTimeInMinutes'] = this.avgTimeInMinutes;
// data['AvgTimeInHHMM'] = this.avgTimeInHHMM;
// data['DistanceInKilometers'] = this.distanceInKilometers;
// data['Latitude'] = this.latitude;
// data['Longitude'] = this.longitude;
// data['Phonenum?ber'] = this.phonenum?ber;
// data['ProjectImageURL'] = this.projectImageURL;
// data['ProjectName'] = this.projectName;
// return data;
// }
//}

@ -11,6 +11,7 @@ import 'package:hmg_patient_app_new/core/utils/utils.dart';
import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart'; import 'package:hmg_patient_app_new/features/authentication/authentication_view_model.dart';
import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/book_appointments/book_appointments_view_model.dart';
import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart'; import 'package:hmg_patient_app_new/features/doctor_filter/doctor_filter_view_model.dart';
import 'package:hmg_patient_app_new/features/emergency_services/emergency_services_view_model.dart';
import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart'; import 'package:hmg_patient_app_new/features/habib_wallet/habib_wallet_view_model.dart';
import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart'; import 'package:hmg_patient_app_new/features/immediate_livecare/immediate_livecare_view_model.dart';
import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart'; import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart';
@ -125,6 +126,9 @@ void main() async {
), ),
ChangeNotifierProvider<DoctorFilterViewModel>( ChangeNotifierProvider<DoctorFilterViewModel>(
create: (_) => getIt.get<DoctorFilterViewModel>(), create: (_) => getIt.get<DoctorFilterViewModel>(),
),
ChangeNotifierProvider<EmergencyServicesViewModel>(
create: (_) => getIt.get<EmergencyServicesViewModel>(),
) )
], child: MyApp()), ], child: MyApp()),
), ),

@ -6,21 +6,21 @@ import 'package:hmg_patient_app_new/core/utils/size_utils.dart';
import 'package:hmg_patient_app_new/core/utils/utils.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/string_extensions.dart';
import 'package:hmg_patient_app_new/extensions/widget_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/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart'; import 'package:hmg_patient_app_new/presentation/emergency_services/nearest_er_page.dart';
import 'package:hmg_patient_app_new/theme/colors.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/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart';
import 'package:provider/provider.dart';
class EmergencyServicesPage extends StatelessWidget { class EmergencyServicesPage extends StatelessWidget {
EmergencyServicesPage({super.key}); const EmergencyServicesPage({super.key});
LocationUtils? locationUtils;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
locationUtils = getIt.get<LocationUtils>();
locationUtils!.isShowConfirmDialog = true;
return CollapsingListView( return CollapsingListView(
title: "Emergency Services", title: "Emergency Services",
requests: () {}, requests: () {},
@ -79,13 +79,7 @@ class EmergencyServicesPage extends StatelessWidget {
Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h), Utils.buildSvgWithAssets(icon: AppAssets.forward_chevron_icon, width: 13.h, height: 13.h),
], ],
).onPress(() { ).onPress(() {
locationUtils!.getLocation(onSuccess: (position) { context.read<EmergencyServicesViewModel>().navigateToNearestERPage();
Navigator.of(context).push(
CustomPageRoute(
page: NearestErPage(),
),
);
});
}), }),
), ),
SizedBox(height: 16.h), SizedBox(height: 16.h),

@ -1,5 +1,22 @@
import 'package:easy_localization/easy_localization.dart' show tr, StringTranslateExtension;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hmg_patient_app_new/core/app_assets.dart';
import 'package:hmg_patient_app_new/core/utils/size_utils.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/emergency_services/model/resp_model/ProjectAvgERWaitingTime.dart';
import 'package:hmg_patient_app_new/generated/locale_keys.g.dart';
import 'package:hmg_patient_app_new/presentation/emergency_services/widgets/nearestERItem.dart'
show NearestERItem;
import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart';
import 'package:hmg_patient_app_new/widgets/input_widget.dart';
import 'package:provider/provider.dart' show Selector, WatchContext, ReadContext;
import '../../core/enums.dart' show SelectionTypeEnum;
import '../../core/utils/debouncer.dart' show Debouncer;
class NearestErPage extends StatefulWidget { class NearestErPage extends StatefulWidget {
const NearestErPage({super.key}); const NearestErPage({super.key});
@ -9,11 +26,84 @@ class NearestErPage extends StatefulWidget {
} }
class _NearestErPageState extends State<NearestErPage> { class _NearestErPageState extends State<NearestErPage> {
final TextEditingController searchText = TextEditingController();
final Debouncer debouncer = Debouncer(milliseconds: 500);
@override
void dispose() {
debouncer.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CollapsingListView( return CollapsingListView(
title: "Nearest ER", title: "Nearest ER".needTranslation,
child: Container(), child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
TextInputWidget(
labelText: LocaleKeys.search.tr(),
hintText: 'Type any facility name'.needTranslation,
controller: searchText,
onChange: (value) {
debouncer.run((){
context.read<EmergencyServicesViewModel>().filterErList(value??'');
});
},
isEnable: true,
prefix: null,
autoFocus: false,
isBorderAllowed: false,
keyboardType: TextInputType.text,
isAllowLeadingIcon: true,
leadingIcon: AppAssets.search_icon,
padding: EdgeInsets.symmetric(
vertical: ResponsiveExtension(10).h,
horizontal: ResponsiveExtension(15).h,
),
),
Selector<EmergencyServicesViewModel,
bool>(
selector: (___, viewModel) => viewModel.isERListLoading,
builder: (_, isLoading, __) =>
Selector<EmergencyServicesViewModel,
List<ProjectAvgERWaitingTime>>(
selector: (___, viewModel) => viewModel.nearestERFilteredList,
shouldRebuild: (previous, next) => previous != next,
builder: (_, value, __) {
if (isLoading || value.isNotEmpty) {
return ListView.separated(
padding: EdgeInsets.only(top: 24.h),
itemCount: isLoading ? 6 : value.length,
separatorBuilder: (____, _____) =>
SizedBox(height: 16.h,),
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 100.0,
child: FadeInAnimation(child: NearestERItem(
isLoading: isLoading,
nearestERItem: isLoading
? ProjectAvgERWaitingTime()
: value[index],
))));
},
);
}else {
return Center(child: Utils.getNoDataWidget(context, noDataText: "No nearest Er Arround you".needTranslation));
}
}
),
),
],
).paddingAll(16.h),
)
); );
} }
} }

@ -0,0 +1,130 @@
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: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/model/resp_model/ProjectAvgERWaitingTime.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/chip/app_custom_chip_widget.dart';
import 'package:provider/provider.dart';
class NearestERItem extends StatelessWidget {
final ProjectAvgERWaitingTime nearestERItem;
final bool isLoading;
const NearestERItem({ super.key,
required this.nearestERItem,
required this.isLoading
}) ;
@override
Widget build(BuildContext context) {
return Container(
decoration: RoundedRectangleBorder().toSmoothCornerDecoration(
color: Colors.white,
customBorder: BorderRadius.only(
topLeft: Radius.circular(24.h),
topRight: Radius.circular(24.h),
),
),
child: Padding(
padding: EdgeInsets.all(16.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
(isLoading || nearestERItem.projectImageURL?.isEmpty == true)
? Container(
width: 24.h,
height: 24.h,
decoration: BoxDecoration(
color: Colors.grey.shade300,
shape: BoxShape.circle,
),
).toShimmer2(isShow: isLoading)
: Utils.buildImgWithNetwork(
url: nearestERItem.projectImageURL ?? '',
iconColor: Colors.transparent,
).circle(24.h).toShimmer2(isShow: isLoading),
const SizedBox(width: 12),
Expanded(
child: (nearestERItem.projectName?.toText16(
color: AppColors.textColor,
weight: FontWeight.w600,
) ??
SizedBox.shrink()).toShimmer2(isShow: isLoading),
),
// TODO: Add hospital icon logic here if needed
],
),
SizedBox(height: 8.h),
Row(
spacing: 8.h,
children: [
AppCustomChipWidget(
labelText: "${nearestERItem.distanceInKilometers} km".needTranslation,
icon: AppAssets.location,
iconHasColor: false,
labelPadding: EdgeInsetsDirectional.only(start: 4.h, end: 0.h),
padding: EdgeInsets.all(8.h),
).toShimmer2(isShow: isLoading),
AppCustomChipWidget(
labelText: "Expected waiting time: ${nearestERItem.getTime()} mins".needTranslation,
icon: AppAssets.waiting_time_clock,
iconHasColor: false,
labelPadding: EdgeInsetsDirectional.only(start: 4.h, end: 0.h),
padding: EdgeInsets.all(8.h),
).toShimmer2(isShow: isLoading),
],
),
SizedBox(height: 16.h),
Row(
children: [
Expanded(
child: CustomButton(
text: "View Location on Google Maps".needTranslation,
iconSize: 18.h,
icon: AppAssets.location,
onPressed: () {
context.read<EmergencyServicesViewModel>().openDirections(destLat: double.parse(nearestERItem.latitude??"0.0"), destLng: double.parse(nearestERItem.longitude??"0.0") );
},
backgroundColor: AppColors.secondaryLightRedColor,
borderColor: AppColors.secondaryLightRedColor,
textColor: AppColors.primaryRedColor,
iconColor: AppColors.primaryRedColor,
height: 40.h,
fontSize: 14,
fontWeight: FontWeight.w500,
).toShimmer2(isShow: isLoading),
),
SizedBox(width: 8.h),
SizedBox(
height: 40.h,
width: 40.h,
child: CustomButton(
text: '',
iconSize: 18.h,
icon: AppAssets.call_fill,
onPressed: () {
context.read<EmergencyServicesViewModel>().openDialer( nearestERItem.phonenumber??"");
},
backgroundColor: AppColors.greyColor,
iconColor: AppColors.textColor,
borderColor: AppColors.greyColor,
height: 40.h,
).toShimmer2(isShow: isLoading),
),
],
),
],
),
),
);
}
}

@ -67,19 +67,23 @@ class CustomButton extends StatelessWidget {
children: [ children: [
if (icon != null) if (icon != null)
Padding( Padding(
padding: EdgeInsets.only(right: 8.h, left: 8.h), padding: text.isNotEmpty ?EdgeInsets.only(right: 8.h, left: 8.h): EdgeInsets.zero,
child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize), child: Utils.buildSvgWithAssets(icon: icon!, iconColor: iconColor, isDisabled: isDisabled, width: iconSize, height: iconSize),
), ),
Padding(
padding: EdgeInsets.only(top: 0), Visibility(
child: Text( visible: text.isNotEmpty,
text, child: Padding(
overflow: textOverflow, padding: EdgeInsets.only(top: 0),
style: context.dynamicTextStyle( child: Text(
fontSize: fontSize.fSize, text,
color: isDisabled ? textColor.withOpacity(0.5) : textColor, overflow: textOverflow,
letterSpacing: 0, style: context.dynamicTextStyle(
fontWeight: fontWeight, fontSize: fontSize.fSize,
color: isDisabled ? textColor.withOpacity(0.5) : textColor,
letterSpacing: 0,
fontWeight: fontWeight,
),
), ),
), ),
), ),

Loading…
Cancel
Save