diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index a1f4062..f5aa29d 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -513,12 +513,6 @@ var PATIENT_ER_INSERT_PRES_ORDER = 'Services/Patients.svc/REST/PatientER_InsertP var BLOOD_DONATION_REGISTER_BLOOD_TYPE = 'Services/PatientVarification.svc/REST/BloodDonation_RegisterBloodType'; var ADD_USER_AGREEMENT_FOR_BLOOD_DONATION = 'Services/PatientVarification.svc/REST/AddUserAgreementForBloodDonation'; -// HHC RC SERVICES -var HHC_GET_ALL_SERVICES_RC = "api/HHC/getallhhc"; -var ADD_HHC_ORDER_RC = "api/HHC/add"; -var GET_ALL_HHC_ORDERS_RC = 'api/hhc/list'; -var UPDATE_HHC_ORDER_RC = 'api/hhc/update'; - // RRT RC SERVICES var ADD_RRT_ORDER_RC = "api/rrt/add"; var GET_ALL_RRT_ORDERS_RC = "api/rrt/list"; @@ -858,3 +852,7 @@ class ApiConsts { static final String appGeneralId = "Cs2020@2016\$2958"; static final String sessionID = 'TMRhVmkGhOsvamErw'; } + +class ApiKeyConstants { + static final String googleMapsApiKey = 'AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng'; +} diff --git a/lib/presentation/appointments/appointment_details_page.dart b/lib/presentation/appointments/appointment_details_page.dart index e620783..4c725aa 100644 --- a/lib/presentation/appointments/appointment_details_page.dart +++ b/lib/presentation/appointments/appointment_details_page.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; @@ -26,11 +26,7 @@ import 'package:hmg_patient_app_new/presentation/appointments/widgets/appointmen import 'package:hmg_patient_app_new/presentation/appointments/widgets/ask_doctor_request_type_select.dart'; import 'package:hmg_patient_app_new/presentation/book_appointment/widgets/appointment_calendar.dart'; import 'package:hmg_patient_app_new/presentation/contact_us/feedback_page.dart'; -import 'package:hmg_patient_app_new/presentation/lab/lab_orders_page.dart'; -import 'package:hmg_patient_app_new/presentation/medical_file/widgets/lab_rad_card.dart'; import 'package:hmg_patient_app_new/presentation/prescriptions/prescription_detail_page.dart'; -import 'package:hmg_patient_app_new/presentation/prescriptions/prescriptions_list_page.dart'; -import 'package:hmg_patient_app_new/presentation/radiology/radiology_orders_page.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; @@ -208,7 +204,7 @@ class _AppointmentDetailsPageState extends State { borderRadius: BorderRadius.circular(24.r), // Todo: what is this???? Api Key??? 😲 child: Image.network( - "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=AIzaSyB6TERnxIr0yJ3qG4ULBZbu0sAD4tGqtng", + "https://maps.googleapis.com/maps/api/staticmap?center=${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&zoom=14&size=350x165&maptype=roadmap&markers=color:red%7C${widget.patientAppointmentHistoryResponseModel.latitude},${widget.patientAppointmentHistoryResponseModel.longitude}&key=${ApiKeyConstants.googleMapsApiKey}", fit: BoxFit.contain, ), ), @@ -286,17 +282,17 @@ class _AppointmentDetailsPageState extends State { ) // : SizedBox.shrink() : GridView( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 3, - crossAxisSpacing: 16.h, - mainAxisSpacing: 16.w, - mainAxisExtent: 115.h, - ), - physics: NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, - shrinkWrap: true, - children: [ - MedicalFileCard( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + crossAxisSpacing: 16.h, + mainAxisSpacing: 16.w, + mainAxisExtent: 115.h, + ), + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + shrinkWrap: true, + children: [ + MedicalFileCard( label: LocaleKeys.labResults.tr(context: context), textColor: AppColors.blackColor, backgroundColor: AppColors.whiteColor, @@ -385,13 +381,15 @@ class _AppointmentDetailsPageState extends State { ); Navigator.of(context).push( CustomPageRoute( - page: PrescriptionDetailPage(isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel), + page: PrescriptionDetailPage( + isFromAppointments: true, prescriptionsResponseModel: patientPrescriptionsResponseModel), ), ); } else { showCommonBottomSheetWithoutHeight( context, - child: Utils.getErrorWidget(loadingText: "You don't have any prescriptions for this appointment.".needTranslation), + child: Utils.getErrorWidget( + loadingText: "You don't have any prescriptions for this appointment.".needTranslation), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, @@ -412,11 +410,11 @@ class _AppointmentDetailsPageState extends State { // Navigator.of(context).push( // CustomPageRoute( // page: VaccineListPage(), - // ), - // ); - }), - ], - ), + // ), + // ); + }), + ], + ), // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ diff --git a/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart index b6164d9..3be209b 100644 --- a/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart +++ b/lib/presentation/comprehensive_checkup/cmc_selection_review_page.dart @@ -2,7 +2,6 @@ import 'dart:developer'; 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_state.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; @@ -22,6 +21,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/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/map/location_map_widget.dart'; import 'package:maps_launcher/maps_launcher.dart'; import 'package:provider/provider.dart'; @@ -155,100 +155,26 @@ class _CmcSelectionReviewPageState extends State { } Widget _buildHospitalMap(HospitalsModel selectedHospital) { - final String lat = selectedHospital.latitude ?? "0.0"; - final String lng = selectedHospital.longitude ?? "0.0"; + final String lat = ((selectedHospital.latitude == null || selectedHospital.latitude!.isEmpty) ? "0.0" : selectedHospital.latitude) ?? "0.0"; + final String lng = ((selectedHospital.longitude == null || selectedHospital.longitude!.isEmpty) ? "0.0" : selectedHospital.longitude) ?? "0.0"; log("selectedHospital: $lng and $lat"); if (lat == "0.0" || lng == "0.0") return SizedBox.shrink(); - final String staticMapUrl = - "https://maps.googleapis.com/maps/api/staticmap?center=$lat,$lng&zoom=16&size=600x300&maptype=roadmap&markers=color:red%7C$lat,$lng&key=AIzaSyCyDbWUM9d_sBUGIE8PcuShzPaqO08NSC8"; - - return Stack( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(12.r), - child: Image.network( - staticMapUrl, - height: 200.h, - width: double.infinity, - fit: BoxFit.cover, - loadingBuilder: (context, child, loadingProgress) { - if (loadingProgress == null) return child; - return Container( - height: 200.h, - decoration: BoxDecoration( - color: AppColors.bgScaffoldColor, - borderRadius: BorderRadius.circular(12.r), - ), - child: Center( - child: CircularProgressIndicator( - color: AppColors.primaryRedColor, - ), - ), - ); - }, - errorBuilder: (context, error, stackTrace) { - return Container( - height: 200.h, - decoration: BoxDecoration( - color: AppColors.bgScaffoldColor, - borderRadius: BorderRadius.circular(12.r), - ), - child: Center( - child: Icon( - Icons.error_outline, - size: 48.h, - color: AppColors.greyTextColor, - ), - ), - ); - }, - ), - ), - Positioned( - bottom: 12.h, - right: 12.w, - child: InkWell( - onTap: () => _launchDirections(selectedHospital), - child: Container( - padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h), - decoration: BoxDecoration( - color: AppColors.whiteColor, - borderRadius: BorderRadius.circular(1000.r), - boxShadow: [ - BoxShadow( - color: Color.fromARGB(26, 0, 0, 0), - blurRadius: 8, - offset: Offset(0, 2), - ), - ], - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Utils.buildSvgWithAssets( - icon: AppAssets.directions_icon, - width: 16.w, - height: 16.h, - ), - SizedBox(width: 6.w), - Text( - "Get Directions".needTranslation, - style: TextStyle( - fontSize: 12.f, - fontWeight: FontWeight.w600, - color: AppColors.blackColor, - letterSpacing: -0.4, - ), - ), - ], - ), - ), - ), - ), - ], + final appState = getIt.get(); + final isArabic = appState.isArabic(); + final hospitalName = isArabic ? (selectedHospital.nameN ?? selectedHospital.name ?? '') : (selectedHospital.name ?? ''); + + return LocationMapWidget( + latitude: double.parse(lat), + longitude: double.parse(lng), + address: hospitalName, + title: "Hospital Location".needTranslation, + showTitle: false, + showAddress: false, + padding: EdgeInsets.zero, + onDirectionsTap: () => _launchDirections(selectedHospital), ); } diff --git a/lib/presentation/home_health_care/hhc_procedures_page.dart b/lib/presentation/home_health_care/hhc_procedures_page.dart index e4a6656..bdc2e45 100644 --- a/lib/presentation/home_health_care/hhc_procedures_page.dart +++ b/lib/presentation/home_health_care/hhc_procedures_page.dart @@ -13,9 +13,11 @@ import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_order_detail_page.dart'; import 'package:hmg_patient_app_new/presentation/home_health_care/hhc_selection_review_page.dart'; import 'package:hmg_patient_app_new/presentation/home_health_care/widgets/hhc_ui_selection_helper.dart'; +import 'package:hmg_patient_app_new/services/navigation_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/map/map_utility_screen.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import 'package:shimmer/shimmer.dart'; @@ -78,6 +80,15 @@ class _HhcProceduresPageState extends State { } final canCancel = order.statusId == 1 || order.statusId == 2; + final isArabic = getIt.get().isArabic(); + + // Extract services from orderselectedservice + List selectedServices = []; + if (order.orderselectedservice != null) { + if (order.orderselectedservice is List) { + selectedServices = order.orderselectedservice as List; + } + } return Container( width: double.infinity, @@ -128,6 +139,85 @@ class _HhcProceduresPageState extends State { ], ), + // Services List + if (selectedServices.isNotEmpty) ...[ + SizedBox(height: 16.h), + Container( + padding: EdgeInsets.all(12.w), + decoration: BoxDecoration( + color: AppColors.bgScaffoldColor, + borderRadius: BorderRadius.circular(12.r), + border: Border.all( + color: AppColors.greyColor.withAlpha(51), + width: 1, + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.medical_services_outlined, + size: 18.h, + color: AppColors.primaryRedColor, + ), + SizedBox(width: 6.w), + "Requested Services".needTranslation.toText14( + weight: FontWeight.w600, + color: AppColors.blackColor, + ), + ], + ), + SizedBox(height: 8.h), + ...selectedServices.asMap().entries.map((entry) { + int index = entry.key; + var service = entry.value; + String serviceName = ''; + + if (service is Map) { + if (isArabic) { + serviceName = service['SelectedServiceNameAR']?.toString() ?? + service['selectedServiceNameAR']?.toString() ?? + service['SelectedServiceName']?.toString() ?? + service['selectedServiceName']?.toString() ?? + ''; + } else { + serviceName = service['SelectedServiceName']?.toString() ?? service['selectedServiceName']?.toString() ?? ''; + } + } + + return Padding( + padding: EdgeInsets.only(top: index > 0 ? 6.h : 0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + margin: EdgeInsets.only(top: 6.h), + width: 6.w, + height: 6.w, + decoration: BoxDecoration( + color: AppColors.primaryRedColor, + shape: BoxShape.circle, + ), + ), + SizedBox(width: 8.w), + Expanded( + child: serviceName.toText14( + color: AppColors.blackColor, + weight: FontWeight.w500, + maxlines: 3, + ), + ), + ], + ), + ); + }).toList(), + ], + ), + ), + ], + SizedBox(height: 12.h), // Info message @@ -206,36 +296,6 @@ class _HhcProceduresPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 8.h), - if (viewModel.selectedHhcServices.isNotEmpty) ...[ - SizedBox(height: 16.h), - Container( - margin: EdgeInsets.symmetric(horizontal: 16.w), - padding: EdgeInsets.all(16.w), - decoration: RoundedRectangleBorder().toSmoothCornerDecoration( - color: AppColors.greyLightColor, - borderRadius: 16.r, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - "Selected Services".needTranslation.toText12( - color: AppColors.textColorLight, - fontWeight: FontWeight.w600, - ), - "${viewModel.selectedHhcServices.length} service(s) selected".toText14( - isBold: true, - weight: FontWeight.bold, - ), - ], - ), - ], - ), - ), - ], - SizedBox(height: 16.h), Text( 'Select Services'.needTranslation, @@ -304,18 +364,35 @@ class _HhcProceduresPageState extends State { ); }, ), - // Summary Section ], ); }, ); } - void _proceedWithSelectedService() { + void _proceedWithSelectedService() async { + final navigationServices = getIt.get(); + final appState = getIt.get(); final hmgServicesViewModel = context.read(); if (hmgServicesViewModel.selectedHhcServices.isNotEmpty) { hmgServicesViewModel.setSelectedServiceForHhcOrder(hmgServicesViewModel.selectedHhcServices.first); - Navigator.of(context).pushReplacement( + + bool result = await navigationServices.push( + CustomPageRoute( + page: MapUtilityScreen( + confirmButtonString: "Submit Request ".needTranslation, + titleString: "Select Location", + subTitleString: "Please select the location".needTranslation, + isGmsAvailable: appState.isGMSAvailable, + ), + direction: AxisDirection.down), + ); + + if (!result) { + return; + } + + Navigator.of(context).push( CustomPageRoute( page: HhcSelectionReviewPage(selectedServices: hmgServicesViewModel.selectedHhcServices), direction: AxisDirection.left, @@ -359,16 +436,48 @@ class _HhcProceduresPageState extends State { if (pendingOrder == null && hmgServicesViewModel.selectedHhcServices.isNotEmpty) { return SafeArea( top: false, - child: Padding( - padding: EdgeInsets.only(left: 16.w, right: 16.w, top: 12.h), - child: CustomButton( - borderWidth: 0, - text: "Next".needTranslation, - onPressed: _proceedWithSelectedService, - textColor: AppColors.whiteColor, - borderRadius: 12.r, - borderColor: Colors.transparent, - padding: EdgeInsets.symmetric(vertical: 14.h), + child: Container( + color: AppColors.whiteColor, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Selected Services Summary Widget + Container( + margin: EdgeInsets.symmetric(horizontal: 16.w, vertical: 4.h), + padding: EdgeInsets.all(16.w), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + "Selected Services".needTranslation.toText12( + color: AppColors.textColorLight, + fontWeight: FontWeight.w600, + ), + "${hmgServicesViewModel.selectedHhcServices.length} service(s) selected".toText14( + isBold: true, + weight: FontWeight.bold, + ), + ], + ), + ], + ), + ), + // Next Button + Padding( + padding: EdgeInsets.only(left: 16.w, right: 16.w), + child: CustomButton( + borderWidth: 0, + text: "Next".needTranslation, + onPressed: _proceedWithSelectedService, + textColor: AppColors.whiteColor, + borderRadius: 12.r, + borderColor: Colors.transparent, + padding: EdgeInsets.symmetric(vertical: 14.h), + ), + ), + ], ), ), ); diff --git a/lib/presentation/home_health_care/hhc_selection_review_page.dart b/lib/presentation/home_health_care/hhc_selection_review_page.dart index 7baeec0..8e9ba90 100644 --- a/lib/presentation/home_health_care/hhc_selection_review_page.dart +++ b/lib/presentation/home_health_care/hhc_selection_review_page.dart @@ -10,13 +10,14 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/hmg_services/hmg_services_view_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/req_models/cmc_create_new_order_req_model.dart'; import 'package:hmg_patient_app_new/features/hmg_services/models/resq_models/get_cmc_services_resp_model.dart'; -import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/hospital_model.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/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; +import 'package:hmg_patient_app_new/widgets/map/location_map_widget.dart'; import 'package:maps_launcher/maps_launcher.dart'; import 'package:provider/provider.dart'; @@ -57,6 +58,8 @@ class _HhcSelectionReviewPageState extends State { children: [ _buildSelectedServicesCard(isArabic), SizedBox(height: 16.h), + _buildLocationMap(), + SizedBox(height: 16.h), ], ), ), @@ -104,6 +107,36 @@ class _HhcSelectionReviewPageState extends State { ); } + Widget _buildLocationMap() { + final locationViewModel = getIt.get(); + final geocodeResponse = locationViewModel.geocodeResponse; + final mapCapturedLocation = locationViewModel.mapCapturedLocation; + + // Check if we have location data + if (mapCapturedLocation == null || (mapCapturedLocation.lat == 0.0 && mapCapturedLocation.lng == 0.0)) { + return SizedBox.shrink(); + } + + final double lat = mapCapturedLocation.lat ?? 0.0; + final double lng = mapCapturedLocation.lng ?? 0.0; + + if (lat == 0.0 || lng == 0.0) return SizedBox.shrink(); + + // Get address from geocode response + String address = "Selected Location".needTranslation; + if (geocodeResponse != null && geocodeResponse.results.isNotEmpty) { + address = geocodeResponse.results.first.formattedAddress; + } + + return LocationMapWidget( + latitude: lat, + longitude: lng, + address: address, + title: "Service Location".needTranslation, + onDirectionsTap: () => _launchDirectionsToLocation(lat, lng, address), + ); + } + Widget _buildBottomButton() { return SafeArea( top: false, @@ -133,16 +166,9 @@ class _HhcSelectionReviewPageState extends State { ); } - void _launchDirections(HospitalsModel selectedHospital) { - final double lat = double.parse(selectedHospital.latitude ?? "0.0"); - final double lng = double.parse(selectedHospital.longitude ?? "0.0"); - + void _launchDirectionsToLocation(double lat, double lng, String locationName) { if (lat != 0.0 && lng != 0.0) { - MapsLauncher.launchCoordinates( - lat, - lng, - selectedHospital.name ?? "Hospital", - ); + MapsLauncher.launchCoordinates(lat, lng, locationName); } } diff --git a/lib/widgets/map/location_map_widget.dart b/lib/widgets/map/location_map_widget.dart new file mode 100644 index 0000000..4cf5eae --- /dev/null +++ b/lib/widgets/map/location_map_widget.dart @@ -0,0 +1,199 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/api_consts.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/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; +import 'package:hmg_patient_app_new/theme/colors.dart'; +import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:maps_launcher/maps_launcher.dart'; + +/// A reusable location map widget that displays a static map with address and directions button +/// Can be used in any review screen (HHC, CMC, etc.) +class LocationMapWidget extends StatelessWidget { + /// The latitude coordinate of the location + final double latitude; + + /// The longitude coordinate of the location + final double longitude; + + /// The formatted address or location name to display + final String address; + + /// The title to show above the map (e.g., "Service Location", "Hospital Location") + final String title; + + /// The zoom level for the map (default: 14) + final int zoomLevel; + + /// The size of the map image (default: 350x165) + final String mapSize; + + /// Optional callback when directions button is pressed + /// If not provided, will use default MapsLauncher + final VoidCallback? onDirectionsTap; + + /// Whether to show the address container (default: true) + final bool showAddress; + + /// Whether to show the title (default: true) + final bool showTitle; + + /// Custom map type (default: roadmap) + final String mapType; + final EdgeInsets? padding; + + const LocationMapWidget({ + super.key, + required this.latitude, + required this.longitude, + required this.address, + required this.title, + this.zoomLevel = 14, + this.mapSize = '350x165', + this.onDirectionsTap, + this.showAddress = true, + this.showTitle = true, + this.mapType = 'roadmap', + this.padding, + }); + + void _defaultLaunchDirections() { + if (latitude != 0.0 && longitude != 0.0) { + MapsLauncher.launchCoordinates(latitude, longitude, address); + } + } + + @override + Widget build(BuildContext context) { + final String staticMapUrl = + "https://maps.googleapis.com/maps/api/staticmap?center=$latitude,$longitude&zoom=$zoomLevel&size=$mapSize&maptype=$mapType&markers=color:red%7C$latitude,$longitude&key=${ApiKeyConstants.googleMapsApiKey}"; + + return Container( + decoration: RoundedRectangleBorder().toSmoothCornerDecoration( + color: AppColors.whiteColor, + borderRadius: 16.r, + ), + padding: padding ?? EdgeInsets.all(16.w), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Title + if (showTitle) ...[ + Text( + title, + style: TextStyle( + fontSize: 16.f, + fontWeight: FontWeight.w700, + color: AppColors.blackColor, + letterSpacing: -0.5, + ), + ), + SizedBox(height: 12.h), + ], + + // Address display + if (showAddress) ...[ + Container( + padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 14.h), + decoration: BoxDecoration( + color: AppColors.bgScaffoldColor, + borderRadius: BorderRadius.circular(12.r), + border: Border.all( + color: AppColors.greyColor.withAlpha(51), + width: 1, + ), + ), + child: Row( + children: [ + Icon(Icons.location_on, color: AppColors.primaryRedColor, size: 20.h), + SizedBox(width: 8.w), + Expanded( + child: Text( + address, + style: TextStyle( + fontSize: 14.f, + fontWeight: FontWeight.w500, + color: AppColors.blackColor, + letterSpacing: -0.4, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + SizedBox(height: 16.h), + ], + + // Map display with bottom button overlay + Stack( + children: [ + ClipRRect( + clipBehavior: Clip.hardEdge, + borderRadius: BorderRadius.circular(24.r), + child: Image.network( + staticMapUrl, + fit: BoxFit.contain, + loadingBuilder: (context, child, loadingProgress) { + if (loadingProgress == null) return child; + return Container( + height: 165.h, + decoration: BoxDecoration( + color: AppColors.bgScaffoldColor, + borderRadius: BorderRadius.circular(24.r), + ), + child: Center( + child: CircularProgressIndicator( + color: AppColors.primaryRedColor, + ), + ), + ); + }, + errorBuilder: (context, error, stackTrace) { + return Container( + height: 165.h, + decoration: BoxDecoration( + color: AppColors.bgScaffoldColor, + borderRadius: BorderRadius.circular(24.r), + ), + child: Center( + child: Icon( + Icons.error_outline, + size: 48.h, + color: AppColors.greyTextColor, + ), + ), + ); + }, + ), + ), + Positioned( + bottom: 0, + child: SizedBox( + width: MediaQuery.of(context).size.width * 0.785, + child: CustomButton( + text: "Get Directions".needTranslation, + onPressed: onDirectionsTap ?? _defaultLaunchDirections, + backgroundColor: AppColors.textColor.withValues(alpha: 0.8), + borderColor: AppColors.textColor.withValues(alpha: 0.01), + textColor: AppColors.whiteColor, + fontSize: 14.f, + fontWeight: FontWeight.w500, + borderRadius: 12.r, + padding: EdgeInsets.symmetric(horizontal: 10.w), + height: 40.h, + icon: AppAssets.directions_icon, + iconColor: AppColors.whiteColor, + iconSize: 14.h, + ).paddingAll(12.h), + ), + ), + ], + ), + ], + ), + ); + } +} diff --git a/lib/widgets/map/map_utility_screen.dart b/lib/widgets/map/map_utility_screen.dart index 87c99fc..e05c104 100644 --- a/lib/widgets/map/map_utility_screen.dart +++ b/lib/widgets/map/map_utility_screen.dart @@ -1,29 +1,13 @@ -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'; @@ -42,14 +26,22 @@ import '../../../widgets/common_bottom_sheet.dart'; /// 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; final String titleString; final String subTitleString; final bool isGmsAvailable; final VoidCallback? onCrossClicked; + final VoidCallback? onSubmitted; - const MapUtilityScreen({super.key, required this.confirmButtonString, required this.titleString, required this.subTitleString, required this.isGmsAvailable, this.onCrossClicked}); + const MapUtilityScreen({ + super.key, + required this.confirmButtonString, + required this.titleString, + required this.subTitleString, + required this.isGmsAvailable, + this.onCrossClicked, + this.onSubmitted, + }); @override Widget build(BuildContext context) { @@ -57,15 +49,9 @@ class MapUtilityScreen extends StatelessWidget { 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(); + 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(); }), ), ), @@ -74,38 +60,26 @@ class MapUtilityScreen extends StatelessWidget { children: [ if (isGmsAvailable) GMSMap( - currentLocation: - context.read().getGMSLocation(), - onCameraMoved: (value) => context - .read() - .handleGMSMapCameraMoved(value), - onCameraIdle: - context.read().handleOnCameraIdle, + currentLocation: context.read().getGMSLocation(), + onCameraMoved: (value) => context.read().handleGMSMapCameraMoved(value), + onCameraIdle: context.read().handleOnCameraIdle, myLocationEnabled: true, - inputController: - context.read().gmsController, + inputController: context.read().gmsController, showCenterMarker: true, ) else HMSMap( - currentLocation: - context.read().getHMSLocation(), - onCameraMoved: (value) => context - .read() - .handleHMSMapCameraMoved(value), - onCameraIdle: - context.read().handleOnCameraIdle, + currentLocation: context.read().getHMSLocation(), + onCameraMoved: (value) => context.read().handleHMSMapCameraMoved(value), + onCameraIdle: context.read().handleOnCameraIdle, myLocationEnabled: false, - inputController: - context.read().hmsController, + 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(); + child: Utils.buildSvgWithAssets(icon: AppAssets.closeBottomNav, width: 32.h, height: 32.h).onPress(() { + onCrossClicked?.call(); // context // .read() // .flushPickupInformation(); @@ -120,8 +94,7 @@ class MapUtilityScreen extends StatelessWidget { Widget FixedBottomSheet(BuildContext context) { return GestureDetector( - onVerticalDragUpdate: (details){ - }, + onVerticalDragUpdate: (details) {}, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.end, @@ -149,20 +122,23 @@ class MapUtilityScreen extends StatelessWidget { spacing: 4.h, children: [ titleString.toText21( - weight: FontWeight.w600, - color: AppColors.textColor, - ), + weight: FontWeight.w600, + color: AppColors.textColor, + ), subTitleString.needTranslation.toText12( - fontWeight: FontWeight.w500, - color: AppColors.greyTextColor, - ) + 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); + text: confirmButtonString.needTranslation, + onPressed: () { + if (onSubmitted != null) { + onSubmitted!(); + } else { + Navigator.pop(context, true); + } }, ) ], @@ -190,8 +166,6 @@ class MapUtilityScreen extends StatelessWidget { ); } - - textPlaceInput(context) { return Consumer(builder: (_, vm, __) { return SizedBox( @@ -200,8 +174,7 @@ class MapUtilityScreen extends StatelessWidget { labelText: "Enter Pickup Location Manually".needTranslation, hintText: "Enter Pickup Location".needTranslation, controller: TextEditingController( - text: vm.geocodeResponse?.results.first.formattedAddress ?? - vm.selectedPrediction?.description, + text: vm.geocodeResponse?.results.first.formattedAddress ?? vm.selectedPrediction?.description, ), leadingIcon: AppAssets.location_pickup, isAllowLeadingIcon: true,