// import 'dart:io' as html; import 'dart:io' as io; import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:frontend/utils/auth_utils.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:universal_html/html.dart' as html; import 'dart:async'; import 'dart:typed_data'; // Import for Uint8List import 'package:flutter/foundation.dart'; import '../../../config/apiUrl.dart'; import '../../../services/apiService.dart'; import '../../../widgets/custom_user_form.dart'; import '../../../widgets/custom_user_form.dart'; import '../../../widgets/custom_user_travel.dart'; class TravellerDetails extends StatefulWidget { final bool isDesktop; final Map controllers; final Map errorMessages; final bool isViewMode; final Map? travelDetails; final String? passportFileUrlFromApi; const TravellerDetails({ Key? key, required this.controllers, required this.errorMessages, required this.isDesktop, required this.isViewMode, this.travelDetails, this.passportFileUrlFromApi, }) : super(key: key); @override TravellerDetailsState createState() => TravellerDetailsState(); } class TravellerDetailsState extends State { final ApiService apiService = ApiService(); html.File? passportFile; late List isExpandedList; int? expandedIndex; bool isCountryLoading = true; late final userId; String? selectedFileNames; Uint8List? passportDocumentBytes; String? passportFileUrlFromApi; // String? selectedPurpose; // String? selectedCountry; String? selectedLocalIdType; List countryList = []; String? selectedDomesticSeat; String? selectedIntenationalSeat; String? SelectedIdType; Map airlineControllers = {}; Map flierNumberControllers = {}; List> hotelLoyaltyEntries = []; int _rowhotelCounter = 1; List> frequentFlierEntries = []; int _rowFlierCounter = 1; List> visaEntries = []; int _rowVisaCounter = 1; Map countryMap = {}; late List? apiCountryData; late List? apiHotelsData; late List? apiAirlineCountryData; Map? apiData; final Map controllers = {}; final List headers = [ "Passport Details", // "Local ID Details", "Visa Details", "Frequent Flyer Information", "Hotel Loyalty Membership", "Preferences Domestic", "Preferences International", "Other Details", "Forex Details" ]; //To Create Controllers List dataHeader = [ "Fname", "Lname", "nationality", "d_meal_pref", "i_meal_pref", "d_additonal_Info", "i_additonal_Info", "passportNumber", "emergency_contact", "placeOfIssue", "passportDoc", "dateOfIssue", "dateOfExpiry", "forex_card_num", "full_name_as_id", "local_id_num", "forex_expiry_date" ]; @override void initState() { super.initState(); // userId = getUserId(); apiCountryData = null; apiHotelsData = []; apiAirlineCountryData = []; apiData = null; for (var field in dataHeader) { controllers[field] = TextEditingController(); } fetchCountries(); fetchHotels(); loadCountryList(); fetchApiData(); // Delay adding the row until after the first frame WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { addHotelLoyaltyEntry(); addFrequentFlierEntry(); addVisaEntry(); }); updateTravel(); }); } void _clearError(String field) { setState(() { widget.errorMessages.remove(field); }); } //-------------------------Hotel Loyalty -------------------------------- void addHotelLoyaltyEntry() { final localId = _rowhotelCounter++; hotelLoyaltyEntries.add({ "local_id": localId, "id": null, // to be set when backend responds "hotel_name": null, // "controller_hotel": TextEditingController(), "controller_membership": TextEditingController(), "is_active": "1" }); setState(() {}); } void removeHotelLoyaltyEntry(Map entry) { print("removeHotelLoyaltyEntry- $entry"); setState(() { entry["is_active"] = "0"; }); } //-------------------------Frequent Flier-------------------------------- void addFrequentFlierEntry() { final localId = _rowFlierCounter++; frequentFlierEntries.add({ "local_id": localId, "id": null, // to be set when backend responds // "controller_airline": null, "airline": null, "controller_flier_number": TextEditingController(), "is_active": "1" }); setState(() {}); } void removeFrequentFlierEntry(Map entry) { print("removeFrequentFlierEntrydd- $entry"); setState(() { entry["is_active"] = "0"; }); } //-------------------------Visa -------------------------------- void addVisaEntry() { final localId = _rowVisaCounter++; visaEntries.add({ "local_id": localId, "id": null, // to be set when backend responds "country_code": null, "visa_type_id": null, "controller_valid_from": TextEditingController(), "controller_valid_upto": TextEditingController(), "is_active": "1" }); setState(() {}); } void removeVisaEntry(Map entry) { print("removeFrequentFlierEntrydd- $entry"); setState(() { entry["is_active"] = "0"; }); } @override void dispose() { for (var entry in hotelLoyaltyEntries) { // entry["controller_hotel"].dispose(); entry["controller_membership"].dispose(); } for (var entry in frequentFlierEntries) { // entry["controller_airline"].dispose(); entry["controller_flier_number"].dispose(); } for (var entry in visaEntries) { entry["controller_valid_from"].dispose(); entry["controller_valid_upto"].dispose(); } super.dispose(); } void disposeController(dynamic controller) { if (controller is TextEditingController) { controller.dispose(); } } Map get travel_Detials { Map data = { // "given_Name": controllers["Fname"]?.text, // "last_Name": controllers["Lname"]?.text, // "nationality": controllers["nationality"]?.text, "passport_number": controllers["passportNumber"]?.text ?? '', "place_of_issue": controllers["placeOfIssue"]?.text, "date_of_issue": controllers["dateOfIssue"]?.text, "date_of_expiry": controllers["dateOfExpiry"]?.text, "d_meal_preference": controllers["d_meal_pref"]?.text, "d_seat_preference": selectedDomesticSeat, "d_additonalInfo": controllers["d_additonal_Info"]?.text, "i_meal_preference": controllers["i_meal_pref"]?.text, "i_seat_preference": selectedIntenationalSeat, "i_additonalInfo": controllers["i_additonal_Info"]?.text, "emergency_contact_number": controllers["emergency_contact"]?.text, "forex_pre_paid_card_number": controllers["forex_card_num"]?.text, "forex_expiry_date": controllers["forex_expiry_date"]?.text, // "frequent_flier_information": [], "frequent_flier_information": frequentFlierList, "hotel_membership": hotelMembershipList, "visa_details": visaDetailsList, // "local_id_type": selectedLocalIdType, // "local_id_num": controllers["local_id_num"]?.text, // "full_name_as_id": controllers["full_name_as_id"]?.text, }; return data; } // List> get hotelMembershipList { // final mappedList = hotelLoyaltyEntries.map((entry) { // return { // "id": entry["id"], // "hotel_name": entry["controller_hotel"].text ?? '', // "membership_number": entry["controller_membership"].text ?? '', // "created_by": null, // "updated_by": null, // "is_active": entry["is_active"] ?? "1", // }; // }).toList(); // // final allEntriesEmpty = mappedList.every((entry) => // (entry["hotel_name"] as String).trim().isEmpty && // (entry["membership_number"] as String).trim().isEmpty); // // return allEntriesEmpty ? [] : mappedList; // } List> get hotelMembershipList { final mappedList = hotelLoyaltyEntries.map((entry) { final membership_number = entry["controller_membership"]; // final hotelName = entry["controller_hotel"]; print("membership_number- ${membership_number}"); return { "id": entry["id"], "hotel_id": entry["hotel_id"] ?? "", // "hotel_name": entry["hotel_id"] ?? "", "hotel_name": entry["hotel_name"] ?? "", // "hotel_name": hotelName is TextEditingController ? hotelName.text : "", "membership_number": membership_number is TextEditingController ? membership_number.text : "", "created_by": null, "updated_by": null, "is_active": entry["is_active"] ?? "1", }; }).toList(); final allEntriesEmpty = mappedList.every((entry) => (entry["hotel_id"] as String).trim().isEmpty && (entry["membership_number"] as String).trim().isEmpty); return allEntriesEmpty ? [] : mappedList; } // List> get frequentFlierList { // return frequentFlierEntries // // .where((entry) => entry["is_active"] != "0") // ✅ Only active entries // .map((entry) { // return { // "id": entry["id"], // "airline": entry["airline"], // "frequent_flier_number": entry["controller_flier_number"].text, // "created_by": null, // "updated_by": null, // "is_active": entry["is_active"] ?? "1", // }; // }).toList(); // } List> get frequentFlierList { final mappedList = frequentFlierEntries.map((entry) { final frequent_flier_number = entry["controller_flier_number"]; print("frequent_flier_number- $frequent_flier_number"); return { "id": entry["id"], "airline": entry["airline"] ?? "", "frequent_flier_number": frequent_flier_number is TextEditingController ? frequent_flier_number.text : "", "created_by": null, "updated_by": null, "is_active": entry["is_active"] ?? "1", }; }).toList(); final allEntriesEmpty = mappedList.every((entry) => (entry["airline"] as String).trim().isEmpty && (entry["frequent_flier_number"] as String).trim().isEmpty); return allEntriesEmpty ? [] : mappedList; } // List> get visaDetailsList { // return visaEntries // // .where((entry) => entry["is_active"] != "0") // ✅ Only active entries // .map((entry) { // return { // "id": entry["id"], // "country_code": entry["country_code"], // "visa_type_id": entry["visa_type_id"], // "valid_from": entry["controller_valid_from"].text, // "valid_upto": entry["controller_valid_upto"].text, // // "valid_from": entry["controller_valid_from"] != null // // ? entry["controller_valid_from"].text // // : "", // // "valid_upto": entry["controller_valid_upto"] != null // // ? entry["controller_valid_upto"].text // // : "", // "created_by": null, // "updated_by": null, // "is_active": entry["is_active"] ?? "1", // }; // }).toList(); // } List> get visaDetailsList { final mappedList = visaEntries.map((entry) { final fromController = entry["controller_valid_from"]; final uptoController = entry["controller_valid_upto"]; return { "id": entry["id"], "country_code": entry["country_code"], // ✅ string value "visa_type_id": entry["visa_type_id"], // ✅ string value "valid_from": fromController is TextEditingController ? fromController.text : "", "valid_upto": uptoController is TextEditingController ? uptoController.text : "", "created_by": null, "updated_by": null, "is_active": entry["is_active"] ?? "1", }; }).toList(); final allEntriesEmpty = mappedList.every((entry) => (entry["country_code"] == null || entry["country_code"].toString().trim().isEmpty) && (entry["visa_type_id"] == null || entry["visa_type_id"].toString().trim().isEmpty) && (entry["valid_from"] == null || entry["valid_from"].toString().trim().isEmpty) && (entry["valid_upto"] == null || entry["valid_upto"].toString().trim().isEmpty)); return allEntriesEmpty ? [] : mappedList; } void updateTravel() { print("widget.travelDetails"); print(widget.travelDetails); if (widget.travelDetails != null) { print("Updatewidget.travelDetails"); print(widget.travelDetails); controllers["passportNumber"]?.text = widget.travelDetails?["passport_number"] ?? ""; controllers["placeOfIssue"]?.text = widget.travelDetails?["place_of_issue"] ?? ""; controllers["dateOfIssue"]?.text = widget.travelDetails?["date_of_issue"] ?? ""; controllers["dateOfExpiry"]?.text = widget.travelDetails?["date_of_expiry"] ?? ""; controllers["d_meal_pref"]?.text = widget.travelDetails?["d_meal_preference"] ?? ""; controllers["i_meal_pref"]?.text = widget.travelDetails?["i_meal_preference"] ?? ""; controllers["d_additonal_Info"]?.text = widget.travelDetails?["d_additonalInfo"] ?? ""; controllers["i_additonal_Info"]?.text = widget.travelDetails?["i_additonalInfo"] ?? ""; selectedIntenationalSeat = widget.travelDetails?["i_seat_preference"] ?? ""; selectedDomesticSeat = widget.travelDetails?["d_seat_preference"] ?? ""; controllers["emergency_contact"]?.text = widget.travelDetails?["emergency_contact_number"] ?? ""; controllers["forex_card_num"]?.text = widget.travelDetails?["forex_pre_paid_card_number"] ?? ""; controllers["forex_expiry_date"]?.text = widget.travelDetails?["forex_expiry_date"] ?? ""; String? apiDocPath = widget.travelDetails?["passport_document"]; if (apiDocPath != null && apiDocPath.isNotEmpty) { passportFileUrlFromApi = apiDocPath; selectedFileNames = apiDocPath.split('/').last; passportFile = null; } else { passportFileUrlFromApi = null; selectedFileNames = null; passportFile = null; } //---------------------------------------------------- // 🚨 Hotel Membership Logic (pre-fill for edit) // for (var entry in hotelLoyaltyEntries) { // // entry["controller_hotel"].dispose(); // entry["controller_membership"].dispose(); // } // hotelLoyaltyEntries.clear(); for (var entry in hotelLoyaltyEntries) { disposeController(entry["controller_membership"]); // (entry["controller_membership"] as TextEditingController?)?.dispose(); } if (!mounted) return; // ✅ avoids context errors print(MediaQuery.of(context).size); // // for (var entry in hotelLoyaltyEntries) { // (entry["controller_membership"] as TextEditingController?)?.dispose(); // } hotelLoyaltyEntries.clear(); final hotelMembershipList = widget.travelDetails?['hotel_membership']; if (hotelMembershipList != null && hotelMembershipList is List) { for (var item in hotelMembershipList) { if (item["is_active"]?.toString() == "0") continue; final localId = _rowhotelCounter++; final hotelDataId = item["hotel_id"]; hotelLoyaltyEntries.add({ "local_id": localId, "id": item["id"], // "controller_hotel": // TextEditingController(text: item["hotel_name"] ?? ""), "hotel_name": item["hotel_name"] ?? "", "hotel_id": hotelDataId ?? "", // "controller_membership": // TextEditingController(text: item["membership_number"] ?? ""), "controller_membership": TextEditingController(text: item["membership_number"] ?? ""), }); } if (hotelLoyaltyEntries.isEmpty) { addHotelLoyaltyEntry(); } } //---------------------------------------------------- // 🚨 Visa Logic (pre-fill for edit) for (var entry in visaEntries) { entry["controller_valid_from"]?.dispose(); entry["controller_valid_upto"]?.dispose(); } visaEntries.clear(); final visaDetailsList = widget.travelDetails?['visa_details']; if (visaDetailsList != null && visaDetailsList is List) { for (var item in visaDetailsList) { if (item["is_active"]?.toString() == "0") continue; final localId = _rowVisaCounter++; visaEntries.add({ "local_id": localId, "id": item["id"], "country_code": item["country_code"], "visa_type_id": item["visa_type_id"], "controller_valid_from": TextEditingController(text: item["valid_from"] ?? ""), "controller_valid_upto": TextEditingController(text: item["valid_upto"] ?? ""), }); } if (visaEntries.isEmpty) { addVisaEntry(); } } // ---------------------------------------------------- // 🚨 Frequent Flier Logic (pre-fill for edit) // for (var entry in frequentFlierEntries) { // // entry["controller_airline"].dispose(); // // entry["controller_flier_number"].dispose(); // (entry["controller_flier_number"] as TextEditingController?)?.dispose(); // } for (var entry in frequentFlierEntries) { entry["controller_flier_number"]?.dispose(); } frequentFlierEntries.clear(); print(MediaQuery.of(context).size); final frequentFlierList = widget.travelDetails?['frequent_flier_information']; if (frequentFlierList != null && frequentFlierList is List) { for (var item in frequentFlierList) { if (item["is_active"]?.toString() == "0") continue; final localId = _rowFlierCounter++; final airlineCode = item["airline"]; frequentFlierEntries.add({ "local_id": localId, "id": item["id"], "airline": airlineCode ?? "", "controller_flier_number": TextEditingController( text: item["frequent_flier_number"] ?? ""), }); } if (frequentFlierEntries.isEmpty) { addFrequentFlierEntry(); } } //---------------------------------------------------- setState(() {}); } } Future fetchCountries() async { try { List countries = await apiService.fetchCountryList(); setState(() { apiCountryData = countries; }); } catch (e) { print('Error fetching country list: $e'); } } Future fetchHotels() async { try { List countries = await apiService.fetchHotelsList(); setState(() { apiHotelsData = countries; }); } catch (e) { print('Error fetching country list: $e'); } } Future loadCountryList() async { final newTripType = "2"; final result = await apiService.fetchFlightsCountryList(newTripType); print("resultFlight: $result "); setState(() { apiAirlineCountryData = result; }); // print("ResultCountry : $result"); // // // Create a map: Country_Code -> "City, Airport" // Map tempCountryMap = {}; // // for (var country in result) { // String city = country['City'] ?? ''; // String airport = country['Airport'] ?? ''; // String displayName = '${country['City']} - ${country['Airport']}'; // // tempCountryMap[country['Code']] = displayName; // } // // setState(() { // countryMap = tempCountryMap; // Update the map // isCountryLoading = false; // }); } Future fetchApiData() async { try { final data = await apiService.fetchMasterDropdown(); print("fetchApiData - $data"); if (data is! Map) { throw Exception("Invalid response format: expected a Map"); } if (!mounted) return; setState(() { apiData = data; }); } catch (e) { print('Error fetching role list: $e'); } } @override Widget build(BuildContext context) { return Container( height: MediaQuery.of(context).size.height, decoration: BoxDecoration( border: Border( top: BorderSide( color: Colors.blueGrey.shade300, // Set your desired color width: 0.09, // Set border thickness ), ), ), child: SingleChildScrollView( scrollDirection: Axis.vertical, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ...List.generate(headers.length, (index) { final isExpanded = expandedIndex == index; return Card( color: isExpanded ? Color(0xFFFFFDF3) : Colors.white, elevation: isExpanded ? 0.9 : 0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), margin: const EdgeInsets.symmetric(vertical: 5), child: InkWell( focusColor: Color(0xFFFFFDF3), hoverColor: Color(0xFFFFFDF3), highlightColor: Color(0xFFFFFDF3), // removes grey highlight splashColor: Colors.white, borderRadius: BorderRadius.circular(12), onTap: () { setState(() { expandedIndex = isExpanded ? null : index; }); }, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( headers[index], style: GoogleFonts.poppins( fontSize: widget.isDesktop ? 12 : 11, fontWeight: FontWeight.w500, color: Colors.black87, ), ), Icon( isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down, color: isExpanded ? Colors.black87 : Colors.grey[600], ) ], ), AnimatedSize( duration: const Duration(milliseconds: 250), curve: Curves.easeInOut, child: ConstrainedBox( constraints: isExpanded ? const BoxConstraints() : const BoxConstraints(maxHeight: 0), child: Padding( padding: const EdgeInsets.only(top: 8), child: Container( width: double.infinity, padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), ), child: _buildExpandedContent(index), ), ), ), ), ], ), ), ), ); }), ], ), ), ); } Widget _buildExpandedContent(int index) { switch (headers[index]) { case "Passport Details": return _buildPassportDatas(widget.isDesktop); // case "Local ID Details": // return _buildLocalIdDetails(widget.isDesktop); case "Visa Details": return _buildVisaDetails(widget.isDesktop); case "Frequent Flyer Information": return _buildFrequentFlierInfo(widget.isDesktop); case "Hotel Loyalty Membership": return _buildHotelLoyalty(widget.isDesktop); case "Preferences Domestic": return _buildPreferencesDomestic(widget.isDesktop); // case "Preferences International": return _buildPreferencesInternational(widget.isDesktop); case "Other Details": return _buildOtherDetails(widget.isDesktop); case "Forex Details": return _buildForexDetails(widget.isDesktop); default: return Text( "Form fields for ${headers[index]} go here", style: GoogleFonts.poppins(fontSize: 12), ); } } //------------------------------------Passport Details----------------------------------- Widget _buildPassportDatas(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // SizedBox( // height: 10, // ), // _buildPassportDataRow1(widget.isDesktop), SizedBox( height: 10, ), _buildPassportDataRow2(widget.isDesktop), SizedBox( height: 10, ), _buildPassportDataRow3(widget.isDesktop), ], ); } Widget _buildPassportDataRow1(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildFirstNameField(), Spacer(), buildLastNameField(), Spacer(), // Space after Last Name buildNationality(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildFirstNameField(), SizedBox(height: 8), // Vertical space buildLastNameField(), SizedBox(height: 8), buildNationality(), ], ), ); } Widget _buildPassportDataRow2(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildPassportNumber(), Spacer(), buildPlaceOfIssue(), Spacer(), /// Space after Last Name buildDateOfIssue(), SizedBox(width: 25), buildDateOfExpiry() ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildPassportNumber(), SizedBox(height: 8), // Vertical space buildPlaceOfIssue(), SizedBox(height: 8), buildDateOfIssue(), SizedBox(height: 8), buildDateOfExpiry() ], ), ); } Widget _buildPassportDataRow3(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildPassportDocument(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildPassportDocument(), ], ), ); } Widget buildFirstNameField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "First Name", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["Fname"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("first_name"); }, decoration: InputDecoration( labelText: "FirstName", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (widget.errorMessages["first_name"] != null) ...[ SizedBox(height: 5), // Space before error message Text( widget.errorMessages["first_name"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ); } Widget buildLastNameField() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Last Name", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["Lname"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("last_name"); }, decoration: InputDecoration( labelText: "LastName", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (widget.errorMessages["last_name"] != null) ...[ SizedBox(height: 5), // Space before error message Text( widget.errorMessages["last_name"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ); } Widget buildNationality() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Nationality", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["nationality"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("last_name"); }, decoration: InputDecoration( labelText: "Nationality", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), if (widget.errorMessages["last_name"] != null) ...[ SizedBox(height: 5), // Space before error message Text( widget.errorMessages["last_name"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ); } Widget buildPassportNumber() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Passport Number", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["passportNumber"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("last_name"); }, decoration: InputDecoration( labelText: "Passport Number", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } Widget buildPlaceOfIssue() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Place of Issue", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["placeOfIssue"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("placeOfIssue"); }, decoration: InputDecoration( labelText: "Place of Issue", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } Widget buildDateOfIssue() { DateTime? _selectedDateOfIssue; Future _selectCheckDateOfIssue(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today) ? _selectedDateOfIssue! : today, firstDate: today, lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedDateOfIssue) { setState(() { _selectedDateOfIssue = pickedDate; controllers["dateOfIssue"]?.text = DateFormat('dd-MM-yyyy').format(pickedDate); }); } } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Date of Issue", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.13 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: widget.isViewMode ? null : () async { await _selectCheckDateOfIssue(context); if (controllers["dateOfIssue"]!.text.isNotEmpty) { setState(() { // errorMessages.remove("start_date"); }); } }, child: AbsorbPointer( child: TextField( controller: controllers["dateOfIssue"], style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), decoration: InputDecoration( labelText: "Select Date", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical: 16), suffixIcon: const Icon(Icons.calendar_today, size: 16, color: Color(0xFF8B8FB2)), ), ), ), ), ), ), ], ); } Widget buildDateOfExpiry() { DateTime? _selectedDateOfExpiry; Future _selectCheckDateOfExpiry(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); // DateTime today = DateTime(now.year now.day, now.month,); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedDateOfExpiry != null && _selectedDateOfExpiry!.isAfter(today) ? _selectedDateOfExpiry! : today, firstDate: today, lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedDateOfExpiry) { setState(() { _selectedDateOfExpiry = pickedDate; controllers["dateOfExpiry"]?.text = DateFormat('dd-MM-yyyy').format(pickedDate); // controllers["dateOfExpiry"]?.text = // DateFormat('dd-MM-yyyy').format(pickedDate); }); } } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Date of Expiry", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.13 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: widget.isViewMode ? null : () async { await _selectCheckDateOfExpiry(context); if (controllers["dateOfExpiry"]!.text.isNotEmpty) { setState(() { // errorMessages.remove("start_date"); }); } }, child: AbsorbPointer( child: TextField( controller: controllers["dateOfExpiry"], style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), decoration: InputDecoration( labelText: "Select Date", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: const EdgeInsets.symmetric(vertical: 16), suffixIcon: const Icon(Icons.calendar_today, size: 16, color: Color(0xFF8B8FB2)), ), ), ), ), ), ), ], ); } Widget buildPassportDocument() { void pickPDFWeb() { html.FileUploadInputElement uploadInput = html.FileUploadInputElement(); uploadInput.accept = '.pdf'; uploadInput.click(); uploadInput.onChange.listen((e) { final file = uploadInput.files!.first; // Ensure the file is a PDF if (!file.type.contains("pdf")) { print("Error: Not a PDF file"); return; } // 🔹 File size check: Ensure it does not exceed 3MB int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes if (file.size > maxFileSize) { print('Error: File size exceeds 3MB'); return; } setState(() { selectedFileNames = file.name; passportFile = file; passportFileUrlFromApi = null; }); print('PDF File selected: ${file.name}'); }); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Passport Document", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: pickPDFWeb, child: Container( padding: EdgeInsets.symmetric(horizontal: 10), alignment: Alignment.centerLeft, child: Text( selectedFileNames ?? "Select Document", style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), overflow: TextOverflow.ellipsis, // Prevents overflow issues ), ), ), ), ), SizedBox(height: 10), if (passportFile != null || passportFileUrlFromApi != null) // Centers the text Container( color: Colors.white, width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.25 : MediaQuery.of(context).size.width * 0.8, child: Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ GestureDetector( onTap: () { print('DOWNLOAD - $passportFile'); if (passportFile != null) { try { // ✅ Step 1: Create a Blob directly from the file // final blob = // html.Blob([passportFile!], 'application/pdf'); // // // ✅ Step 2: Generate a download URL from the Blob // final url = html.Url.createObjectUrlFromBlob(blob); // // // ✅ Step 3: Create an invisible anchor to trigger download // final anchor = html.AnchorElement(href: url) // ..setAttribute( // "download", selectedFileNames ?? "document.pdf") // ..style.display = "none"; // // // ✅ Step 4: Add anchor to DOM and click it // html.document.body!.append(anchor); // anchor.click(); // // // ✅ Step 5: Clean up // anchor.remove(); // html.Url.revokeObjectUrl(url); // Create a blob from the response body final blob = html.Blob([passportFile!]); // Generate a download URL for the blob final url = html.Url.createObjectUrlFromBlob(blob); // Create a link element to trigger the download final anchor = html.AnchorElement(href: url) ..setAttribute( 'download', selectedFileNames ?? "document.pdf") ..click(); // Revoke the download URL to free up resources html.Url.revokeObjectUrl(url); print("Download triggered successfully!"); } catch (e) { print("Error during download: $e"); } } else if (passportFileUrlFromApi != null) { print("Raw file path: $passportFileUrlFromApi"); // Extract public part of the path starting from "/assets" String cleanedPath = passportFileUrlFromApi!; final index = passportFileUrlFromApi!.indexOf("/assets"); if (index != -1) { cleanedPath = passportFileUrlFromApi!.substring(index); } final fullUrl = 'https://apitest.tripapprovaltool.com$cleanedPath'; print("Final download URL: $fullUrl"); final anchor = html.AnchorElement(href: fullUrl) ..target = '_blank' ..download = selectedFileNames ?? "document.pdf" ..click(); } else { print("No file available to download."); } }, child: Container( padding: const EdgeInsets.all(5), decoration: BoxDecoration( borderRadius: BorderRadius.circular(5), color: Colors.green.shade300, ), child: Row( mainAxisAlignment: MainAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( "Download", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w200, color: Colors.white), overflow: TextOverflow.ellipsis, textAlign: TextAlign .end, // Ensures text is centered within the Text widget ), SizedBox(width: 5), Icon( Icons.download, size: 13, color: Colors.white, ) ], ), ), ) ], ), ), ], ); } //------------------------Local Details---------------------------------------- Widget _buildLocalIdDetails(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 10, ), _buildLocalDetailDataRow1(widget.isDesktop), ], ); } Widget _buildLocalDetailDataRow1(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildLocalIdType(), Spacer(), buildIdNumber(), Spacer(), buildFullNameAsId(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildLocalIdType(), SizedBox(height: 8), buildIdNumber(), SizedBox(height: 8), // Vertical space buildFullNameAsId(), ], ), ); } Widget buildIdNumber() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Id Number", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["local_id_num"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("local_id_num"); }, decoration: InputDecoration( labelText: "Id Number", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } Widget buildLocalIdType() { final List> localIdOptions = [ {"dropdown_value": "Licence"}, {"dropdown_value": "PAN"}, {"dropdown_value": "Voter Card"}, {"dropdown_value": "Aadhar Card"}, ]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Id Type", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 35, width: double.infinity, child: DropdownSearch>( popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, menuProps: const MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), child: Text(item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, )), ); }, ), dropdownDecoratorProps: const DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), ), ), dropdownButtonProps: const DropdownButtonProps( icon: Icon(Icons.arrow_drop_down), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text("Select Seat", // fallback text style: GoogleFonts.poppins( fontSize: 12, color: Colors.grey)); } return Text(selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins(fontSize: 12, color: Colors.black)); }, items: localIdOptions, selectedItem: selectedLocalIdType == null ? null : {"dropdown_value": selectedLocalIdType}, onChanged: widget.isViewMode ? null : (Map? newItem) { if (newItem != null) { setState(() { selectedLocalIdType = newItem['dropdown_value']; }); } }, ), ), ), ], ); } Widget buildFullNameAsId() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Full Name As ID", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["full_name_as_id"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("full_name_as_id"); }, decoration: InputDecoration( labelText: "FullName As ID", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } //------------------------Preferences Domestic --------------------------------------- Widget _buildPreferencesDomestic(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 10, ), _buildDomesticDetailDataRow1(widget.isDesktop), ], ); } Widget _buildDomesticDetailDataRow1(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildDomesticSeatType(), SizedBox(width: 15), buildDomesticMealPref(), SizedBox(width: 15), buildDomesticAddtnlPref(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildDomesticSeatType(), SizedBox(height: 8), buildDomesticMealPref(), SizedBox(height: 8), // Vertical space buildDomesticAddtnlPref(), ], ), ); } Widget buildDomesticSeatType() { final List> seatOptions = [ {"dropdown_value": "Aisle"}, {"dropdown_value": "Window"}, {"dropdown_value": "Middle"}, ]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Seat Preference", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 35, width: double.infinity, child: DropdownSearch>( popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, menuProps: const MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), child: Text(item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, )), ); }, ), dropdownDecoratorProps: const DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), ), ), dropdownButtonProps: const DropdownButtonProps( icon: Icon(Icons.arrow_drop_down), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text("Select Seat", // fallback text style: GoogleFonts.poppins( fontSize: 12, color: Colors.grey)); } return Text(selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins(fontSize: 12, color: Colors.black)); }, items: seatOptions, selectedItem: selectedDomesticSeat == null ? null : {"dropdown_value": selectedDomesticSeat}, onChanged: widget.isViewMode ? null : (Map? newItem) { if (newItem != null) { setState(() { selectedDomesticSeat = newItem['dropdown_value']; }); } }, ), ), ), ], ); } Widget buildDomesticMealPref() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Meal Preference", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["d_meal_pref"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("d_meal_pref"); }, decoration: InputDecoration( labelText: "Meal Preference", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } Widget buildDomesticAddtnlPref() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Additional Information", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.4 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["d_additonal_Info"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("d_additonal_Info"); }, decoration: InputDecoration( labelText: "Please Enter", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } //------------------------Preferences International --------------------------------------- Widget _buildPreferencesInternational(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 10, ), _buildInternationalDetailDataRow1(widget.isDesktop), ], ); } Widget _buildInternationalDetailDataRow1(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildInternationalSeatType(), SizedBox(width: 15), buildInternationalMealPref(), SizedBox(width: 15), buildInternationalAddtnlPref(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildInternationalSeatType(), SizedBox(height: 8), buildInternationalMealPref(), SizedBox(height: 8), // Vertical space buildInternationalAddtnlPref(), ], ), ); } Widget buildInternationalSeatType() { final List> seatOptionsInt = [ {"dropdown_value": "Aisle"}, {"dropdown_value": "Window"}, {"dropdown_value": "Middle"}, ]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Seat Preference", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 35, width: double.infinity, child: DropdownSearch>( popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, menuProps: const MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) { return Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), child: Text(item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, )), ); }, ), dropdownDecoratorProps: const DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5), ), ), // selectedItem: purposeList.cast>().firstWhere( // (item) => item['dropdown_key'] == _selectedIsBillable, // orElse: () => {}, // ), dropdownButtonProps: const DropdownButtonProps( icon: Icon(Icons.arrow_drop_down), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text("Select Seat", // fallback text style: GoogleFonts.poppins( fontSize: 12, color: Colors.grey)); } return Text(selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins(fontSize: 12, color: Colors.black)); }, items: seatOptionsInt, selectedItem: selectedIntenationalSeat == null ? null : {"dropdown_value": selectedIntenationalSeat}, onChanged: widget.isViewMode ? null : (Map? newItem) { if (newItem != null) { setState(() { selectedIntenationalSeat = newItem['dropdown_value']; }); } }, ), ), ), ], ); } Widget buildInternationalMealPref() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Meal Preference", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["i_meal_pref"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("i_meal_pref"); }, decoration: InputDecoration( labelText: "Meal Preference", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } Widget buildInternationalAddtnlPref() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Additional Information", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.4 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["i_additonal_Info"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("i_additonal_Info"); }, decoration: InputDecoration( labelText: "Please Enter", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } //--------------------------Other Details------------------------------- Widget _buildOtherDetails(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 10, ), buildEmergencyContactNum(), ], ); } Widget buildEmergencyContactNum() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Emergency Contact Number", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["emergency_contact"], inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[0-9\s]')), ], // maxLength: 10, enabled: !widget.isViewMode, onChanged: (value) { _clearError("emergency_contact"); }, decoration: InputDecoration( // counterText: '', labelText: "Contact Number", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } //-------------------------Forex Prepaid Card Number------------------------ Widget _buildForexDetails(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 10, ), _buildForexDetailDataRow1(isDesktop), ], ); } Widget _buildForexDetailDataRow1(bool isDesktop) { return Container( color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildForexCardNumber(), SizedBox(width: 15), buildForexExpiryDate(), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildForexCardNumber(), SizedBox(height: 8), buildForexExpiryDate() ], ), ); } Widget buildForexCardNumber() { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Forex Pre-Paid Card Number", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: controllers["forex_card_num"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("forex_card_num"); }, decoration: InputDecoration( labelText: "Card Number", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } Widget buildForexExpiryDate() { DateTime? _selectedExpiryDate; Future _selectExpiryDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); DateTime? pickedDate = await showDatePicker( context: context, initialDate: _selectedExpiryDate != null && _selectedExpiryDate!.isAfter(today) ? _selectedExpiryDate! : today, firstDate: DateTime(1900), lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedExpiryDate) { setState(() { _selectedExpiryDate = pickedDate; // controllers["forex_expiry_date"]?.text = // DateFormat('yyyy-MM-dd').format(pickedDate); controllers["forex_expiry_date"]?.text = DateFormat('dd-MM-yyyy').format(pickedDate); }); } } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Forex Expiry Date", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: widget.isViewMode ? null : () async { await _selectExpiryDate(context); }, child: AbsorbPointer( child: TextField( controller: controllers["forex_expiry_date"], style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), decoration: InputDecoration( labelText: "Select Date", labelStyle: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon(Icons.calendar_today, size: 16, color: Color(0xFF8B8FB2)), ), ), ), ), ), ), ], ); } //-------------------------Frequent Flier Information ------------------------------- Widget _buildFrequentFlierInfo(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ...frequentFlierEntries .where((e) => e["is_active"] != "0") .map((e) => _buildFrequentAirlineDataRow1(e, isDesktop)), IconForAirlineMem(), ], ); } Widget _buildFrequentAirlineDataRow1( Map entry, bool isDesktop) { int localId = entry["local_id"]; return Container( color: Colors.white, padding: const EdgeInsets.only(top: 8.0), child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildAirline(entry), SizedBox(width: 25), buildFrequentFlierInformation(entry), SizedBox(width: 15), IconButton( icon: Icon( Icons.cancel, color: Colors.red, size: 15, ), onPressed: () { setState(() { removeFrequentFlierEntry(entry); }); }, ), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildAirline(entry), SizedBox(height: 8), buildFrequentFlierInformation(entry), SizedBox(width: 15), IconButton( icon: Icon( Icons.cancel, color: Colors.red, size: 15, ), onPressed: () { setState(() { removeFrequentFlierEntry(entry); }); }, ), ], ), ); } Widget buildAirline(Map entry) { late Map countryMap; // Mapping country_code -> country_name late List countryCodes; // List of country codes // countryList = []; bool hasAirlineCountryData = apiAirlineCountryData == null || apiAirlineCountryData!.isEmpty; countryList = apiAirlineCountryData!; print("TestcountryList2 - $countryList"); countryMap = { for (var country in countryList) country['Code'] as String: '${country['City']} - ${country['Airport']}' }; countryCodes = countryMap.keys.toList(); // setState(() { // countryMap = tempCountryMap; // Update the map // isCountryLoading = false; // }); // Map country codes to country names // countryMap = { // // // for (var item in countryList) // item['country_code'] as String: item['country_name'] as String // }; // Extract only country codes for processing //-------------------------- // String? selectedCountry = entry['airline']; String? selectedCode = entry['airline']; String? selectedText = selectedCode != null ? countryMap[selectedCode] : null; print("selectedCode: $selectedCode"); print("selectedText: $selectedText"); print( "items.contains(selectedText): ${countryMap.values.contains(selectedText)}"); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Airline", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.17 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: hasAirlineCountryData ? CircularProgressIndicator() : DropdownSearch( // selectedItem: countryMap[selectedCountry], // selectedItem: entry['airline'] != null // ? countryMap[entry['airline']] // : null, // selectedItem: countryMap.containsKey(entry['airline']) // ? countryMap[entry['airline']] // : null, // selectedItem: countryMap[selectedCode], // selectedItem: countryMap.containsKey(selectedCode) // ? countryMap[selectedCode] // : null, selectedItem: (entry["airline"] != null && countryMap.containsKey(entry["airline"])) ? countryMap[entry["airline"]] : null, popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality menuProps: const MenuProps( backgroundColor: Colors.white, ), constraints: BoxConstraints(maxHeight: 250), itemBuilder: (context, item, isSelected) => Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 6.0), child: Text( item, style: GoogleFonts.poppins(fontSize: 11.5), ), ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Country...", hintStyle: GoogleFonts.poppins(fontSize: 11.5), contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 1, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select Country", style: TextStyle(fontSize: 12), ), ), onChanged: (String? newValue) { setState(() { // Find the country_code based on selected country_name // selectedCountry = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; final selectedCode = countryMap.entries .firstWhere((entry) => entry.value == newValue) .key; entry['airline'] = selectedCode; // final selectedCode = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; // entry['airline'] = selectedCode; }); }, ), ), ), // CustomTextFieldUserTravellerWrapper( // width: widget.isDesktop // ? MediaQuery.of(context).size.width * 0.15 // : null, // isFocused: false, // isDesktop: widget.isDesktop, // child: SizedBox( // height: 40, // child: TextField( // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), // controller: entry["controller_airline"], // enabled: !widget.isViewMode, // onChanged: (value) { // _clearError("first_name"); // }, // decoration: InputDecoration( // labelText: "Airline", // labelStyle: // GoogleFonts.poppins(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 16), // ), // ), // ), // ), ], ); } Widget buildFrequentFlierInformation( Map entry, ) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Frequent Flyer Information", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: entry["controller_flier_number"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("first_name"); }, decoration: InputDecoration( labelText: "Frequent Flyer Information", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } Widget IconForAirlineMem() { return Container( // height: 60, // must define height alignment: Alignment.bottomRight, child: IconButton( icon: Icon( Icons.add_circle, color: Color(0xFF114D8B), size: 30, ), onPressed: () => addFrequentFlierEntry(), tooltip: "Frequent Flyer Information", ), ); } //-------------------------Frequent Loyalty MemberShip Hotels ------------------------------- Widget _buildHotelLoyalty(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // ...hotelLoyaltyEntries.map((entry) { // return _buildFrequentLoyaltyDataRow1(entry, isDesktop); // }), ...hotelLoyaltyEntries .where((e) => e["is_active"] != "0") .map((e) => _buildFrequentLoyaltyDataRow1(e, isDesktop)), IconForLoyaltyMem(), ], ); } Widget _buildFrequentLoyaltyDataRow1( Map entry, bool isDesktop) { int localId = entry["local_id"]; return Container( padding: const EdgeInsets.only(top: 8.0), color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildHotel(entry), SizedBox(width: 25), buildHotelMembershipNum(entry), IconButton( icon: Icon( Icons.cancel, color: Colors.red, size: 15, ), onPressed: () { setState(() { // removeHotelLoyaltyEntry(localId); removeHotelLoyaltyEntry(entry); }); }, ), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildHotel(entry), SizedBox(height: 8), buildHotelMembershipNum(entry), IconButton( icon: Icon( Icons.cancel, color: Colors.red, size: 15, ), onPressed: () { setState(() { // removeHotelLoyaltyEntry(localId); removeHotelLoyaltyEntry(entry); }); }, ), ], ), ); } Widget buildHotel(Map entry) { late Map countryMap; // Mapping country_code -> country_name late List countryCodes; // List of country codes bool hasAirlineCountryData = apiHotelsData == null || apiHotelsData!.isEmpty; countryList = apiHotelsData!; print("TestcountryList2 - $countryList"); countryMap = { for (var country in countryList) country['hotel_id'] as String: '${country['hotel_name']}' }; countryCodes = countryMap.keys.toList(); String? selectedCode = entry['hotel_id']; // String? selectedCode = entry['hotel_id']; String? selectedText = selectedCode != null ? countryMap[selectedCode] : null; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Hotel", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( // width: widget.isDesktop // ? MediaQuery.of(context).size.width * 0.15 // : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: hasAirlineCountryData ? CircularProgressIndicator() : DropdownSearch( selectedItem: (entry["hotel_id"] != null && countryMap.containsKey(entry["hotel_id"])) ? countryMap[entry["hotel_id"]] : null, popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality menuProps: const MenuProps( backgroundColor: Colors.white, ), constraints: BoxConstraints(maxHeight: 250), itemBuilder: (context, item, isSelected) => Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 6.0), child: Text( item, style: GoogleFonts.poppins(fontSize: 11.5), ), ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Hotels...", hintStyle: GoogleFonts.poppins(fontSize: 11.5), contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 1, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select Hotels", style: GoogleFonts.poppins(fontSize: 11), ), ), onChanged: (String? newValue) { setState(() { // Find the country_code based on selected country_name // selectedCountry = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; final selectedCode = countryMap.entries .firstWhere((entry) => entry.value == newValue) .key; print("SelectedHotelId - $selectedCode"); entry['hotel_id'] = selectedCode; // entry["controller_hotel"] = newValue; entry['hotel_name'] = newValue; // final selectedCode = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; // entry['airline'] = selectedCode; }); }, ), ), ), ], ); } Widget buildHotelMembershipNum(Map entry) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Hotel Membership Number", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: TextField( style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), controller: entry["controller_membership"], enabled: !widget.isViewMode, onChanged: (value) { _clearError("first_name"); }, decoration: InputDecoration( labelText: "Hotel Membership Number", labelStyle: GoogleFonts.poppins(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ); } Widget IconForLoyaltyMem() { return Container( height: 60, // must define height alignment: Alignment.bottomRight, child: IconButton( icon: Icon( Icons.add_circle, color: Color(0xFF114D8B), size: 30, ), onPressed: () => addHotelLoyaltyEntry(), tooltip: "Add Hotel Loyalty Membership", ), ); } //-------------------------Visa Details ----------------------------------------- Widget _buildVisaDetails(bool isDesktop) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( height: 10, ), ...visaEntries .where((e) => e["is_active"] != "0") .map((e) => _buildVisaDataRow1(e, isDesktop)), // _buildVisaDataRow1(isDesktop), IconForVisa() ], ); } Widget _buildVisaDataRow1(Map entry, bool isDesktop) { return Container( padding: const EdgeInsets.only(top: 8), color: Colors.white, child: widget.isDesktop ? Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildVisaCountry(entry), SizedBox(width: 25), buildVisaType(entry), SizedBox(width: 15), buildVisaValidFrom(entry), SizedBox(width: 15), buildVisaValidUpTo(entry), IconButton( icon: Icon( Icons.cancel, color: Colors.red, size: 15, ), onPressed: () { setState(() { removeVisaEntry(entry); }); }, ), ], ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ buildVisaCountry(entry), SizedBox(height: 8), buildVisaType(entry), SizedBox(height: 8), buildVisaValidFrom(entry), SizedBox(height: 8), buildVisaValidUpTo(entry), IconButton( icon: Icon( Icons.cancel, color: Colors.red, size: 15, ), onPressed: () { setState(() { // visaRowIds.remove(id); removeVisaEntry(entry); }); }, ), ], ), ); } Widget buildVisaCountry(entry) { late Map countryMap; // Mapping country_code -> country_name late List countryCodes; // List of country codes // countryList = []; countryList = apiCountryData ?? []; print("TestcountryList1 - $countryList"); // Map country codes to country names countryMap = { for (var item in countryList) item['country_code'] as String: item['country_name'] as String }; // Extract only country codes for processing countryCodes = countryMap.keys.toList(); // selectedCountry ??= null; String? selectedCountry = entry['country_code']; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Country", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.17 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: DropdownSearch( selectedItem: countryMap[selectedCountry], popupProps: PopupProps.menu( showSearchBox: true, // Enables search functionality menuProps: const MenuProps( backgroundColor: Colors.white, ), // constraints: BoxConstraints(maxHeight: 250), itemBuilder: (context, item, isSelected) => Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 6.0), child: Text( item, style: GoogleFonts.poppins(fontSize: 11.5), ), ), searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search Country...", hintStyle: GoogleFonts.poppins(fontSize: 11.5), contentPadding: EdgeInsets.symmetric(horizontal: 10), ), ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 1, ), ), ), dropdownBuilder: (context, selectedItem) => Align( // Center-align selected item alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select Country", style: TextStyle(fontSize: 12), ), ), onChanged: (String? newValue) { setState(() { // Find the country_code based on selected country_name // selectedCountry = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; final selectedCode = countryMap.entries .firstWhere((entry) => entry.value == newValue) .key; entry['country_code'] = selectedCode; }); }, ), ), ), ], ); } Widget buildVisaType(entry) { if (apiData == null || apiData?['visa_type_of_visa'] == null) { return Center( child: Transform.scale( scale: 0.5, child: CircularProgressIndicator(), ), ); } // List purposeList = apiData?['visa_type_of_visa'] ?? []; // List purposeList = []; List purposeList = apiData?['visa_type_of_visa']; List> dropdownItems = purposeList .map((item) => DropdownMenuItem( value: item['dropdown_key'], child: Text(item['dropdown_value']), )) .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text("No options available", style: TextStyle(color: Colors.grey)), ), ); } // // Default selected value // selectedPurpose ??= // dropdownItems.isNotEmpty ? dropdownItems.first.value : null; // String? selectedPurpose = entry['visa_type_id']; // 3. Selected value String? selectedPurpose = entry['visa_type_id']?.toString(); // 4. Avoid crash if selected value doesn't match any item if (!dropdownItems.any((item) => item.value == selectedPurpose)) { selectedPurpose = null; } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Visa Type", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.17 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: DropdownButtonFormField( value: selectedPurpose, style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric(horizontal: 10), // Proper padding ), hint: Text("Select"), onChanged: purposeList.isNotEmpty ? (newValue) { setState(() { // selectedPurpose = newValue; entry['visa_type_id'] = newValue; }); print( "Updating form data: Flight -> trip_type -> ${newValue ?? ""}"); } : null, items: dropdownItems, ), ), ), ], ); } Widget buildVisaValidFrom(Map entry) { DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; Future _selectValidFromDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); // Parse date from notifier if available, else use today DateTime initialDate = today; // Use previously selected date if valid if (_selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)) { initialDate = _selectedCheckOutDate!; } final pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: initialDate, lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); entry["controller_valid_from"].text = DateFormat('dd-MM-yyyy').format(pickedDate); }); } } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "ValidFrom", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { await _selectValidFromDate(context); }, child: AbsorbPointer( child: TextField( // focusNode: _dateFocusNode, controller: entry["controller_valid_from"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Date", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon(Icons.calendar_today, size: 16, color: Colors.grey), ), ), ), ), ), ), ], ); } Widget buildVisaValidUpTo(Map entry) { DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; Future _selectValidUpToDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); // Parse date from notifier if available, else use today DateTime initialDate = today; // Use previously selected date if valid if (_selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)) { initialDate = _selectedCheckOutDate!; } final pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: initialDate, lastDate: DateTime(2100), ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); entry["controller_valid_upto"].text = DateFormat('dd-MM-yyyy').format(pickedDate); }); } } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Valid UpTo", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w400, color: Colors.black), ), SizedBox(height: 5), CustomTextFieldUserTravellerWrapper( width: widget.isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, isFocused: false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { await _selectValidUpToDate(context); }, child: AbsorbPointer( child: TextField( // focusNode: _dateFocusNode, // controller: _dateController, controller: entry["controller_valid_upto"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select Date", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon(Icons.calendar_today, size: 16, color: Colors.grey), ), ), ), ), ), ), ], ); } Widget IconForVisa() { return Container( height: 60, // must define height alignment: Alignment.bottomRight, child: IconButton( icon: Icon( Icons.add_circle, color: Color(0xFF114D8B), size: 30, ), onPressed: () => addVisaEntry(), tooltip: "Add Visa Details", ), ); } }