From 5983caf83ae5d3df7073a53ae1ad0f66518c0b90 Mon Sep 17 00:00:00 2001 From: tahaalam Date: Sun, 23 Nov 2025 15:06:35 +0300 Subject: [PATCH 1/2] single map screen added to the project for multiple place usage --- .../emergency_services_view_model.dart | 22 +- .../location/location_view_model.dart | 58 ++++- .../RRT/rrt_map_screen.dart | 2 +- .../call_ambulance/call_ambulance_page.dart | 2 +- .../call_ambulance/tracking_screen.dart | 2 +- .../emergency_services_page.dart | 4 +- lib/widgets/map/{map.dart => gms_map.dart} | 0 lib/widgets/map/map_utility_screen.dart | 235 ++++++++++++++++++ 8 files changed, 317 insertions(+), 8 deletions(-) rename lib/widgets/map/{map.dart => gms_map.dart} (100%) create mode 100644 lib/widgets/map/map_utility_screen.dart diff --git a/lib/features/emergency_services/emergency_services_view_model.dart b/lib/features/emergency_services/emergency_services_view_model.dart index 5dbb89d..ad72ef4 100644 --- a/lib/features/emergency_services/emergency_services_view_model.dart +++ b/lib/features/emergency_services/emergency_services_view_model.dart @@ -20,6 +20,7 @@ import 'package:hmg_patient_app_new/features/emergency_services/models/OrderDisp import 'package:hmg_patient_app_new/features/emergency_services/models/request_model/RRTRequestModel.dart'; import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/EROnlineCheckInPaymentDetailsResponse.dart'; import 'package:hmg_patient_app_new/features/emergency_services/models/resp_model/RRTServiceData.dart'; +import 'package:hmg_patient_app_new/features/location/location_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/facility_selection.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.dart'; import 'package:hmg_patient_app_new/features/emergency_services/models/AmbulanceCallingPlace.dart'; @@ -54,6 +55,7 @@ import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/map/map_utility_screen.dart'; import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:huawei_map/huawei_map.dart' as HMSCameraServices; @@ -1047,12 +1049,26 @@ class EmergencyServicesViewModel extends ChangeNotifier { placeValueInController(); locationUtils!.getLocation( isShowConfirmDialog: true, - onSuccess: (position) { + onSuccess: (position) async { updateBottomSheetState(BottomSheetType.FIXED); - navServices.push( + bool result = await navServices.push( CustomPageRoute( - page: RrtMapScreen(), direction: AxisDirection.down), + page: MapUtilityScreen( + confirmButtonString: "Submit Request ".needTranslation, + titleString: "Select Location", + subTitleString: "Please select the location".needTranslation, + isGmsAvailable: appState.isGMSAvailable, + ), + direction: AxisDirection.down), ); + if(result){ + LocationViewModel locationViewModel = getIt.get(); + GeocodeResponse? response = locationViewModel.geocodeResponse; + PlaceDetails? placeDetails = locationViewModel.placeDetails; + PlacePrediction? placePrediction = locationViewModel.selectedPrediction; + submitRRTRequest(response?.results.first, placeDetails, placePrediction); + } + }); } else{ dialogService.showErrorBottomSheet( diff --git a/lib/features/location/location_view_model.dart b/lib/features/location/location_view_model.dart index e2e0cc5..c6ea34e 100644 --- a/lib/features/location/location_view_model.dart +++ b/lib/features/location/location_view_model.dart @@ -3,6 +3,8 @@ 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/core/app_state.dart'; +import 'package:hmg_patient_app_new/core/dependencies.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'; @@ -18,7 +20,9 @@ class LocationViewModel extends ChangeNotifier { final LocationRepo locationRepo; final ErrorHandlerService errorHandlerService; - LocationViewModel({required this.locationRepo, required this.errorHandlerService}); + LocationViewModel({required this.locationRepo, required this.errorHandlerService}){ + placeValueInController(); + } List predictions = []; PlacePrediction? selectedPrediction; @@ -28,6 +32,26 @@ class LocationViewModel extends ChangeNotifier { Location? mapCapturedLocation; + Completer? gmsController; + Completer? hmsController; + + HMSCameraServices.CameraPosition getHMSLocation() { + return HMSCameraServices.CameraPosition(target: HMSCameraServices.LatLng(getIt().userLat, getIt().userLong), zoom: 18); + } + + + GMSMapServices.CameraPosition getGMSLocation() { + return GMSMapServices.CameraPosition(target: GMSMapServices.LatLng(getIt().userLat, getIt().userLong), zoom: 18); + } + + void placeValueInController() async{ + if (await getIt().isGMSAvailable) { + gmsController = Completer(); + } else { + hmsController = Completer(); + } + } + FutureOr getPlacesPrediction(String input) async { predictions = []; isPredictionLoading= true; @@ -112,5 +136,37 @@ class LocationViewModel extends ChangeNotifier { await getPlaceDetails(placePrediction.placeID); } + void moveToCurrentLocation() { + moveController(Location(lat: getIt().userLat, lng: getIt().userLong)); + } + void moveController(Location location) { + print("moving to location"); + print("gmsController is null or not $gmsController"); + if (getIt().isGMSAvailable) { + gmsController?.future.then((controller) { + controller.animateCamera( + GMSMapServices.CameraUpdate.newCameraPosition( + GMSMapServices.CameraPosition( + target: GMSMapServices.LatLng(location.lat, location.lng), + zoom: 18, + ), + ), + ); + }); + } else { + print("hmsController is null or not $hmsController"); + + hmsController?.future.then((controller) { + controller.animateCamera( + HMSCameraServices.CameraUpdate.newCameraPosition( + HMSCameraServices.CameraPosition( + target: HMSCameraServices.LatLng(location.lat, location.lng), + zoom: 18, + ), + ), + ); + }); + } + } } \ No newline at end of file diff --git a/lib/presentation/emergency_services/RRT/rrt_map_screen.dart b/lib/presentation/emergency_services/RRT/rrt_map_screen.dart index 9e11c31..3a17e5d 100644 --- a/lib/presentation/emergency_services/RRT/rrt_map_screen.dart +++ b/lib/presentation/emergency_services/RRT/rrt_map_screen.dart @@ -26,7 +26,7 @@ import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/ExpandableBo import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart'; -import 'package:hmg_patient_app_new/widgets/map/map.dart'; +import 'package:hmg_patient_app_new/widgets/map/gms_map.dart'; import 'package:provider/provider.dart'; import '../../../widgets/common_bottom_sheet.dart'; diff --git a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart index fea52a3..043c231 100644 --- a/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart +++ b/lib/presentation/emergency_services/call_ambulance/call_ambulance_page.dart @@ -23,7 +23,7 @@ import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/ExpandableBo import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart'; import 'package:hmg_patient_app_new/widgets/input_widget.dart'; import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart'; -import 'package:hmg_patient_app_new/widgets/map/map.dart'; +import 'package:hmg_patient_app_new/widgets/map/gms_map.dart'; import 'package:provider/provider.dart'; import '../../../widgets/common_bottom_sheet.dart'; diff --git a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart index 4a9231a..e5dc0b9 100644 --- a/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart +++ b/lib/presentation/emergency_services/call_ambulance/tracking_screen.dart @@ -14,7 +14,7 @@ import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.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:hmg_patient_app_new/widgets/map/HMSMap.dart'; -import 'package:hmg_patient_app_new/widgets/map/map.dart' show GMSMap; +import 'package:hmg_patient_app_new/widgets/map/gms_map.dart' show GMSMap; import 'package:hmg_patient_app_new/widgets/order_tracking/order_tracking_state.dart'; import 'package:hmg_patient_app_new/widgets/order_tracking/request_tracking.dart'; import 'package:lottie/lottie.dart'; diff --git a/lib/presentation/emergency_services/emergency_services_page.dart b/lib/presentation/emergency_services/emergency_services_page.dart index 8f6fd23..bce7daf 100644 --- a/lib/presentation/emergency_services/emergency_services_page.dart +++ b/lib/presentation/emergency_services/emergency_services_page.dart @@ -256,7 +256,9 @@ class EmergencyServicesPage extends StatelessWidget { isCloseButtonVisible: false, hasBottomPadding: false, backgroundColor: AppColors.primaryRedColor, - callBackFunc: () {}, + callBackFunc: () { + context.read().setTermsAndConditions(false); + }, ); }), ), diff --git a/lib/widgets/map/map.dart b/lib/widgets/map/gms_map.dart similarity index 100% rename from lib/widgets/map/map.dart rename to lib/widgets/map/gms_map.dart diff --git a/lib/widgets/map/map_utility_screen.dart b/lib/widgets/map/map_utility_screen.dart new file mode 100644 index 0000000..1fd9dca --- /dev/null +++ b/lib/widgets/map/map_utility_screen.dart @@ -0,0 +1,235 @@ +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/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/models/AmbulanceCallingPlace.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/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/presentation/appointments/widgets/appointment_doctor_card.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/AddressItem.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/HospitalBottomSheetBody.dart'; +import 'package:hmg_patient_app_new/presentation/emergency_services/call_ambulance/widgets/appointment_bottom_sheet.dart' show AppointmentBottomSheet; +import 'package:hmg_patient_app_new/presentation/emergency_services/widgets/location_input_bottom_sheet.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/CustomSwitch.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/ExpandableBottomSheet.dart'; +import 'package:hmg_patient_app_new/widgets/expandable_bottom_sheet/model/BottomSheetType.dart'; +import 'package:hmg_patient_app_new/widgets/input_widget.dart'; +import 'package:hmg_patient_app_new/widgets/map/HMSMap.dart'; +import 'package:hmg_patient_app_new/widgets/map/gms_map.dart'; +import 'package:provider/provider.dart'; + +import '../../../widgets/common_bottom_sheet.dart'; + +class MapUtilityScreen extends StatelessWidget { + + final String confirmButtonString; + final String titleString; + final String subTitleString; + final bool isGmsAvailable; + final VoidCallback? onCrossClicked; + + const MapUtilityScreen({super.key, required this.confirmButtonString, required this.titleString, required this.subTitleString, required this.isGmsAvailable, this.onCrossClicked}); + + @override + Widget build(BuildContext context) { + return Scaffold( + floatingActionButton: Padding( + padding: EdgeInsetsDirectional.only(end: 8.h, bottom: 68.h), + child: DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, borderRadius: 12.h), + child: Utils.buildSvgWithAssets( + icon: AppAssets.locate_me, width: 24.h, height: 24.h) + .paddingAll(12.h) + .onPress(() { + context + .read() + .moveToCurrentLocation(); + }), + ), + ), + bottomSheet: FixedBottomSheet(context), + body: Stack( + children: [ + if (isGmsAvailable) + GMSMap( + currentLocation: + context.read().getGMSLocation(), + onCameraMoved: (value) => context + .read() + .handleGMSMapCameraMoved(value), + onCameraIdle: + context.read().handleOnCameraIdle, + myLocationEnabled: true, + inputController: + context.read().gmsController, + showCenterMarker: true, + ) + else + HMSMap( + currentLocation: + context.read().getHMSLocation(), + onCameraMoved: (value) => context + .read() + .handleHMSMapCameraMoved(value), + onCameraIdle: + context.read().handleOnCameraIdle, + myLocationEnabled: false, + inputController: + context.read().hmsController, + showCenterMarker: true, + ), + Align( + alignment: AlignmentDirectional.topStart, + child: Utils.buildSvgWithAssets( + icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h) + .onPress(() { + onCrossClicked?.call(); + // context + // .read() + // .flushPickupInformation(); + + Navigator.pop(context, false); + }), + ).paddingOnly(top: 51.h, left: 24.h), + ], + ), + ); + } + + Widget FixedBottomSheet(BuildContext context) { + return GestureDetector( + onVerticalDragUpdate: (details){ + }, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + spacing: 24.h, + children: [ + inputFields(context).paddingSymmetrical(16.h, 0.h), + SizedBox( + child: DecoratedBox( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.scaffoldBgColor, + customBorder: BorderRadius.only( + topLeft: Radius.circular(24.h), + topRight: Radius.circular(24.h), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 24.h, + children: [ + Column( + spacing: 4.h, + children: [ + titleString.toText21( + weight: FontWeight.w600, + color: AppColors.textColor, + ), + subTitleString.needTranslation.toText12( + fontWeight: FontWeight.w500, + color: AppColors.greyTextColor, + ) + ], + ), + CustomButton( + text: confirmButtonString.needTranslation, + onPressed: () { + ///indicates that the screen has resulted success and should be closed + Navigator.pop(context,true); + }, + ) + ], + ).paddingOnly(top: 24.h, bottom: 32.h, left: 24.h, right: 24.h), + ), + ), + ], + ), + ], + ), + ); + } + + leadingIcon(String leadingIcon) { + return Container( + height: 40.h, + width: 40.h, + margin: EdgeInsets.only(right: 10.h), + padding: EdgeInsets.all(8.h), + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + borderRadius: 12.h, + color: AppColors.greyColor, + ), + child: Utils.buildSvgWithAssets(icon: leadingIcon), + ); + } + + + + textPlaceInput(context) { + return Consumer(builder: (_, vm, __) { + return SizedBox( + width: MediaQuery.sizeOf(context).width, + child: TextInputWidget( + labelText: "Enter Pickup Location Manually".needTranslation, + hintText: "Enter Pickup Location".needTranslation, + controller: TextEditingController( + text: vm.geocodeResponse?.results.first.formattedAddress ?? + vm.selectedPrediction?.description, + ), + leadingIcon: AppAssets.location_pickup, + isAllowLeadingIcon: true, + isEnable: false, + prefix: null, + autoFocus: false, + isBorderAllowed: false, + keyboardType: TextInputType.text, + padding: EdgeInsets.symmetric( + vertical: ResponsiveExtension(10).h, + horizontal: ResponsiveExtension(15).h, + ), + ).onPress(() { + openLocationInputBottomSheet(context); + }), + ); + }); + } + + ///decide which field to show first based on the selected calling place + Widget inputFields(BuildContext context) { + return textPlaceInput(context); + } + + openLocationInputBottomSheet(BuildContext context) { + context.read().flushSearchPredictions(); + showCommonBottomSheetWithoutHeight( + title: "".needTranslation, + context, + child: SizedBox( + height: MediaQuery.sizeOf(context).height * .8, + child: LocationInputBottomSheet(), + ), + isFullScreen: false, + isCloseButtonVisible: true, + hasBottomPadding: false, + backgroundColor: AppColors.bottomSheetBgColor, + callBackFunc: () {}, + ); + } +} From cbaea9d5e6114b63087f7446dfc89f258cd2d258 Mon Sep 17 00:00:00 2001 From: tahaalam Date: Sun, 23 Nov 2025 15:13:28 +0300 Subject: [PATCH 2/2] screen documentation added. --- lib/widgets/map/map_utility_screen.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/widgets/map/map_utility_screen.dart b/lib/widgets/map/map_utility_screen.dart index 1fd9dca..87c99fc 100644 --- a/lib/widgets/map/map_utility_screen.dart +++ b/lib/widgets/map/map_utility_screen.dart @@ -31,6 +31,16 @@ import 'package:provider/provider.dart'; import '../../../widgets/common_bottom_sheet.dart'; +/// screen to be used to get the location desired by the user +/// to place the values in the request. +/// [confirmButtonString] button text that will be displayed on the button +/// [titleString] bottom sheet title +/// [subTitleString] bottom sheet subtitle for details +/// [onCrossClicked] if something has to be done if the user close the screen +/// [isGmsAvailable] shows if the device that is running the application is GMS or HMS +/// +/// it results [true] if the user clicks on the submit button +/// and [false] if the user closes the screen without giving the consent to proceed for the request class MapUtilityScreen extends StatelessWidget { final String confirmButtonString;