import 'dart:io'; import 'package:country_code_picker/country_code_picker.dart'; import 'package:customer/themes/show_toast_dialog.dart'; import 'package:customer/utils/utils.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:dropdown_textfield/dropdown_textfield.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/svg.dart'; import 'package:get/get.dart'; import '../../constant/constant.dart'; import '../../controllers/book_parcel_controller.dart'; import '../../controllers/theme_controller.dart'; import '../../models/user_model.dart'; import '../../themes/app_them_data.dart'; import '../../themes/round_button_fill.dart'; import '../../themes/text_field_widget.dart'; import '../../widget/osm_map/map_picker_page.dart'; import '../../widget/place_picker/location_picker_screen.dart'; import '../../widget/place_picker/selected_location_model.dart'; class BookParcelScreen extends StatelessWidget { const BookParcelScreen({super.key}); @override Widget build(BuildContext context) { final themeController = Get.find(); final isDark = themeController.isDark.value; return GetX( init: BookParcelController(), builder: (controller) { return Scaffold( appBar: AppBar( automaticallyImplyLeading: false, backgroundColor: AppThemeData.primary300, title: Padding( padding: const EdgeInsets.only(bottom: 10), child: Row( children: [ GestureDetector( onTap: () => Get.back(), child: Container( height: 42, width: 42, decoration: BoxDecoration(shape: BoxShape.circle, color: AppThemeData.grey50), child: Center(child: Padding(padding: const EdgeInsets.only(left: 5), child: Icon(Icons.arrow_back_ios, color: AppThemeData.grey900, size: 20))), ), ), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Book Your Document Delivery".tr, style: AppThemeData.boldTextStyle(fontSize: 18, color: AppThemeData.grey900)), Text( "Schedule a secure and timely pickup & delivery".tr, maxLines: 1, overflow: TextOverflow.ellipsis, style: AppThemeData.mediumTextStyle(fontSize: 12, color: AppThemeData.grey900), ), ], ), ), ], ), ), ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ selectDeliveryTypeView(controller, isDark, context), const SizedBox(height: 16), buildUploadBoxView(isDark, controller), const SizedBox(height: 16), buildInfoSectionView( title: "Sender Information".tr, locationController: controller.senderLocationController.value, nameController: controller.senderNameController.value, mobileController: controller.senderMobileController.value, noteController: controller.senderNoteController.value, countryCodeController: controller.senderCountryCodeController.value, showWeight: true, isDark: isDark, context: context, controller: controller, onTap: () async { if (Constant.selectedMapType == 'osm') { final result = await Get.to(() => MapPickerPage()); if (result != null) { final firstPlace = result; if (Constant.checkZoneCheck(firstPlace.coordinates.latitude, firstPlace.coordinates.longitude) == true) { final address = firstPlace.address; final lat = firstPlace.coordinates.latitude; final lng = firstPlace.coordinates.longitude; controller.senderLocationController.value.text = address; // ✅ controller.senderLocation.value = UserLocation(latitude: lat, longitude: lng); // ✅ <-- Add this } else { ShowToastDialog.showToast("Service is unavailable at the selected address.".tr); } } } else { Get.to(LocationPickerScreen())!.then((value) async { if (value != null) { SelectedLocationModel selectedLocationModel = value; if (Constant.checkZoneCheck(selectedLocationModel.latLng!.latitude, selectedLocationModel.latLng!.longitude) == true) { controller.senderLocationController.value.text = Utils.formatAddress(selectedLocation: selectedLocationModel); controller.senderLocation.value = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude); } else { ShowToastDialog.showToast("Service is unavailable at the selected address.".tr); } // ✅ <-- Add this } }); } }, ), const SizedBox(height: 16), buildInfoSectionView( title: "Receiver Information".tr, locationController: controller.receiverLocationController.value, nameController: controller.receiverNameController.value, mobileController: controller.receiverMobileController.value, noteController: controller.receiverNoteController.value, countryCodeController: controller.receiverCountryCodeController.value, showWeight: false, isDark: isDark, context: context, controller: controller, onTap: () async { if (Constant.selectedMapType == 'osm') { final result = await Get.to(() => MapPickerPage()); if (result != null) { final firstPlace = result; if (Constant.checkZoneCheck(firstPlace.coordinates.latitude, firstPlace.coordinates.longitude) == true) { final lat = firstPlace.coordinates.latitude; final lng = firstPlace.coordinates.longitude; final address = firstPlace.address; controller.receiverLocationController.value.text = address; // ✅ controller.receiverLocation.value = UserLocation(latitude: lat, longitude: lng); } else { ShowToastDialog.showToast("Service is unavailable at the selected address.".tr); } } } else { Get.to(LocationPickerScreen())!.then((value) async { if (value != null) { SelectedLocationModel selectedLocationModel = value; if (Constant.checkZoneCheck(selectedLocationModel.latLng!.latitude, selectedLocationModel.latLng!.longitude) == true) { controller.receiverLocationController.value.text = Utils.formatAddress(selectedLocation: selectedLocationModel); controller.receiverLocation.value = UserLocation(latitude: selectedLocationModel.latLng!.latitude, longitude: selectedLocationModel.latLng!.longitude); // ✅ <-- Add this } else { ShowToastDialog.showToast("Service is unavailable at the selected address.".tr); } } }); } }, ), const SizedBox(height: 15), RoundedButtonFill( title: "Continue".tr, onPress: () { controller.bookNow(); }, color: AppThemeData.primary300, textColor: AppThemeData.grey900, ), const SizedBox(height: 25), ], ), ), ); }, ); } Widget selectDeliveryTypeView(BookParcelController controller, bool isDark, BuildContext context) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50, border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200), ), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Select delivery type".tr, style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500, fontSize: 13)), const SizedBox(height: 10), InkWell( onTap: () { controller.selectedDeliveryType.value = 'now'; controller.isScheduled.value = false; }, child: Row( children: [ Image.asset("assets/images/image_parcel.png", height: 38, width: 38), const SizedBox(width: 20), Expanded(child: Text("As soon as possible".tr, style: AppThemeData.semiBoldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16))), Icon( controller.selectedDeliveryType.value == 'now' ? Icons.radio_button_checked : Icons.radio_button_off, color: controller.selectedDeliveryType.value == 'now' ? AppThemeData.primary300 : (isDark ? AppThemeData.greyDark500 : AppThemeData.grey500), size: 20, ), ], ), ), const SizedBox(height: 10), InkWell( onTap: () { controller.selectedDeliveryType.value = 'later'; controller.isScheduled.value = true; }, child: Column( children: [ Row( children: [ Image.asset("assets/images/image_parcel_scheduled.png", height: 38, width: 38), const SizedBox(width: 20), Expanded(child: Text("Scheduled".tr, style: AppThemeData.semiBoldTextStyle(color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900, fontSize: 16))), Icon( controller.selectedDeliveryType.value == 'later' ? Icons.radio_button_checked : Icons.radio_button_off, color: controller.selectedDeliveryType.value == 'later' ? AppThemeData.primary300 : (isDark ? AppThemeData.greyDark500 : AppThemeData.grey500), size: 20, ), ], ), if (controller.selectedDeliveryType.value == 'later') ...[ const SizedBox(height: 10), GestureDetector( onTap: () => controller.pickScheduledDate(context), child: TextFieldWidget( hintText: "When to pickup at this address".tr, controller: controller.scheduledDateController.value, enable: false, backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, suffix: const Padding(padding: EdgeInsets.only(right: 10), child: Icon(Icons.calendar_month_outlined)), ), ), const SizedBox(height: 10), GestureDetector( onTap: () => controller.pickScheduledTime(context), child: TextFieldWidget( hintText: "When to pickup at this address".tr, controller: controller.scheduledTimeController.value, enable: false, // onchange: (v) => controller.pickScheduledTime(context), backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, suffix: const Padding(padding: EdgeInsets.only(right: 10), child: Icon(Icons.access_time)), ), ), ], ], ), ), ], ), ); } Widget buildUploadBoxView(bool isDark, BookParcelController controller) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50, border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200), ), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Upload parcel image".tr, style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500, fontSize: 13)), const SizedBox(height: 10), DottedBorder( options: RoundedRectDottedBorderOptions(strokeWidth: 1, radius: const Radius.circular(10), color: isDark ? AppThemeData.greyDark300 : AppThemeData.grey300), child: Container( alignment: Alignment.center, decoration: BoxDecoration(borderRadius: BorderRadius.circular(10), color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 50), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ SvgPicture.asset("assets/icons/ic_upload_parcel.svg", height: 40, width: 40), const SizedBox(height: 10), Text("Upload Parcel Image".tr, style: AppThemeData.mediumTextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900)), const SizedBox(height: 4), Text("Supported: .jpg, .jpeg, .png".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)), Text("Max size 1MB".tr, style: AppThemeData.semiBoldTextStyle(fontSize: 12, color: isDark ? AppThemeData.greyDark800 : AppThemeData.grey800)), const SizedBox(height: 8), RoundedButtonFill( title: "Browse Image".tr, onPress: () { controller.onCameraClick(Get.context!); }, color: AppThemeData.primary300, textColor: AppThemeData.grey900, width: 40, ), ], ), ), ), const SizedBox(height: 10), if (controller.images.isEmpty) const SizedBox(), Wrap( spacing: 10, runSpacing: 10, children: controller.images.map((image) { return Stack( children: [ Container( padding: const EdgeInsets.only(top: 20, right: 20), child: ClipRRect(borderRadius: BorderRadius.circular(8), child: Image.file(File(image.path), width: 70, height: 70, fit: BoxFit.cover)), ), Positioned.fill( top: 0, right: 0, child: Align( alignment: Alignment.topRight, child: IconButton( icon: const Icon(Icons.cancel, color: AppThemeData.danger300, size: 20), onPressed: () { controller.images.remove(image); }, ), ), ), ], ); }).toList(), ), ], ), ); } Widget buildInfoSectionView({ required String title, required TextEditingController locationController, required TextEditingController nameController, required TextEditingController mobileController, required TextEditingController noteController, required TextEditingController countryCodeController, bool showWeight = false, GestureTapCallback? onTap, required bool isDark, required BookParcelController controller, required BuildContext context, }) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(15), color: isDark ? AppThemeData.greyDark50 : AppThemeData.grey50, border: Border.all(color: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200), ), padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 15), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: AppThemeData.boldTextStyle(color: isDark ? AppThemeData.greyDark500 : AppThemeData.grey500, fontSize: 13)), const SizedBox(height: 10), GestureDetector( onTap: onTap, child: TextFieldWidget( hintText: "Your Location".tr, controller: locationController, suffix: const Padding(padding: EdgeInsets.only(right: 10), child: Icon(Icons.location_on_outlined)), backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, enable: false, ), ), const SizedBox(height: 10), TextFieldWidget( hintText: "Name".tr, controller: nameController, backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, ), const SizedBox(height: 10), TextFieldWidget( hintText: "Enter Mobile number".tr, controller: mobileController, textInputType: TextInputType.number, inputFormatters: [FilteringTextInputFormatter.allow(RegExp('[0-9]')), LengthLimitingTextInputFormatter(10)], backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, prefix: Row( mainAxisSize: MainAxisSize.min, children: [ CountryCodePicker( onChanged: (value) { countryCodeController.text = value.dialCode ?? Constant.defaultCountryCode; }, initialSelection: countryCodeController.text.isNotEmpty ? countryCodeController.text : Constant.defaultCountryCode, showCountryOnly: false, showOnlyCountryWhenClosed: false, alignLeft: false, textStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : Colors.black), dialogTextStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900), searchStyle: TextStyle(fontSize: 16, color: isDark ? AppThemeData.greyDark900 : AppThemeData.grey900), dialogBackgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, padding: EdgeInsets.zero, ), // const Icon(Icons.keyboard_arrow_down_rounded, size: 24, color: AppThemeData.grey400), Container(height: 24, width: 1, color: AppThemeData.grey400), const SizedBox(width: 4), ], ), ), if (showWeight) ...[ const SizedBox(height: 10), DropDownTextField( controller: controller.senderWeightController.value, clearOption: false, enableSearch: false, textFieldDecoration: InputDecoration( hintText: "Select parcel Weight".tr, hintStyle: AppThemeData.regularTextStyle(fontSize: 14, color: isDark ? AppThemeData.grey400 : AppThemeData.greyDark400), filled: true, fillColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), border: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppThemeData.grey200)), enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppThemeData.grey200)), focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(10), borderSide: BorderSide(color: AppThemeData.grey200)), ), dropDownList: controller.parcelWeight.map((e) { return DropDownValueModel( name: e.title ?? 'Normal'.tr, value: e.title ?? 'Normal'.tr, // safer to use title string ); }).toList(), onChanged: (val) { if (val is DropDownValueModel) { controller.senderWeightController.value.setDropDown(val); // Link it to the selectedWeight object controller.selectedWeight = controller.parcelWeight.firstWhereOrNull((e) => e.title == val.value); } }, ), ], const SizedBox(height: 10), TextFieldWidget( hintText: "Notes (Optional)".tr, controller: noteController, backgroundColor: isDark ? AppThemeData.surfaceDark : AppThemeData.surface, borderColor: isDark ? AppThemeData.greyDark200 : AppThemeData.grey200, ), ], ), ); } }