import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:frontend/services/apiService.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import '../../utils/auth_utils.dart'; import '../../widgets/custom_confirmation_dialog.dart'; import '../../widgets/custom_radio_button.dart'; import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class FlightScreen extends StatefulWidget { final bool hasAction; final String? tripType; final List> flightData; final Map? apiData; final Map? apiDataForClass; final String? loginUser; final Function(String, bool) onClose; final Function(Map) onSaveFlight; final Map? selectedItem; final ValueNotifier tripTypeNotifier; FlightScreen({ Key? key, required this.apiData, required this.loginUser, required this.onClose, required this.onSaveFlight, required this.selectedItem, required this.flightData, required this.hasAction, this.tripType, required this.tripTypeNotifier, this.apiDataForClass, }) : super(key: key); @override FlightScreenState createState() => FlightScreenState(); } class FlightScreenState extends State { ApiService apiService = ApiService(); final GlobalKey _formKey = GlobalKey(); bool isCountryLoading = true; late Map countryMap; late Map countryCodeMap; late List countryCodes; late ValueNotifier flightFirstTripDateNotifier; late ValueNotifier flightLastTripDateNotifier; String? selectedCountry; List> countryList = []; Map selectedValues = {}; int rowCount = 1; String? selectedTripType; Map selectedClasses = {}; // Store class selection for each trip Map exceptionalClass = {}; // Store class selection for each trip Map selectedFrom = {}; // Store class selection for each trip Map selectedTo = {}; // Store class selection for each trip String? selectedvisa_available; int multiTripRowCount = 1; // Color? layoutColor; Color layoutColor = Colors.grey; List dataHeader = [ "_tripType", "_class", "_from", "_to", "_date", "_visa", "_time", "_comments", ]; Map focusNodes = {}; Map focusStates = {}; Map textControllers = {}; Map fieldErrors = {}; // Holds error messages List> rowBuilders = []; // Holds row widgets List controllers = []; // Dynamic controllers bool _hasInitialTripTypeLoaded = false; Map errorMessages = {}; // forex_pre_paid_card_number @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { loadInitialData(); }); print("TripType - ${widget.tripType}"); // final newTripType = widget.tripTypeNotifier.value; // print("TripType1 - $newTripType"); // // Listen immediately // widget.tripTypeNotifier.addListener(() { // final newTripType = widget.tripTypeNotifier.value; // // if (newTripType != null && newTripType.isNotEmpty) { // if (_hasInitialTripTypeLoaded) { // print("πŸš€ FlightScreen reacting to new trip type: $newTripType"); // loadCountryList(newTripType); // } else { // print("🟑 Skipping initial tripTypeNotifier value: $newTripType"); // _hasInitialTripTypeLoaded = true; // } // } // }); // _initializeRows(); // List purposeList = widget.apiData?['flight_trip_type'] ?? []; // selectedTripType ??= purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; // Get trip type from widget.selectedItem selectedTripType = widget.selectedItem?["trip_type"] as String?; // If null, set it to the first available value from purposeList List purposeList = widget.apiData?['flight_trip_type'] ?? []; if (selectedTripType == null && purposeList.isNotEmpty) { selectedTripType = purposeList.first['dropdown_value'] as String?; } _initializeFields(); getRowCount(); print("Focus Nodes Keys: ${focusNodes.keys.toList()}"); print("Focus States Keys: ${focusStates.keys.toList()}"); print("Text Controllers Keys: ${textControllers.keys.toList()}"); for (var key in focusNodes.keys) { _addFocusListener(focusNodes[key]!, (focus) { setState(() { focusStates[key.replaceFirst("FocusNode", "Focused")] = focus; }); }); } handleUpdateField(); int rowCount = 1; // Default row count for One-way if (selectedTripType == "Roundtrip") { rowCount = 2; // Fixed for Roundtrip } else if (selectedTripType == "Multitrip") { rowCount = multiTripRowCount; // Dynamic row count for Multitrip } // Loop through each row and add listeners to clear errors for (int i = 1; i <= rowCount; i++) { textControllers["_from${i}Controller"]?.addListener( () => _clearError("from_place_$i"), ); textControllers["_to${i}Controller"]?.addListener( () => _clearError("to_place_$i"), ); textControllers["_date${i}Controller"]?.addListener( () => _clearError("date_$i"), ); textControllers["_time${i}Controller"]?.addListener( () => _clearError("time_$i"), ); } // loadCountryList(); flightFirstTripDateNotifier = ValueNotifier(null); flightLastTripDateNotifier = ValueNotifier(null); WidgetsBinding.instance.addPostFrameCallback((_) { final result = getFlightTripDateRange(widget.flightData); flightFirstTripDateNotifier.value = result['firstTripDate']; flightLastTripDateNotifier.value = result['lastTripDate']; final tripTypeValue = widget.tripTypeNotifier.value; if (tripTypeValue != null && tripTypeValue.isNotEmpty) { print("πŸš€ Initial loadCountryList for tripType: $tripTypeValue"); loadCountryList(tripTypeValue); } else { print("⚠️ tripType is null or empty, skipping loadCountryList"); } }); } Map getFlightTripDateRange( List> flightData, ) { final allTrips = flightData .expand((flight) => flight['trips'] ?? []) .whereType>() .toList(); if (allTrips.isEmpty) { return {'firstTripDate': null, 'lastTripDate': null}; } allTrips.sort((a, b) { final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900); final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900); return aDate.compareTo(bDate); }); final firstTrip = allTrips.first; final lastTrip = allTrips.last; return { 'firstTripDate': firstTrip['date'], 'lastTripDate': lastTrip['date'], }; } // // Future loadCountryList() async { // setState(() { // isCountryLoading = true; // }); // // final result = await apiService.fetchFlightsCountryList(); // // if (result is List) { // countryList = // result.map((item) => Map.from(item)).toList(); // countryMap = { // for (var item in countryList) // if (item['country_code'] != null && item['country_name'] != null) // item['country_code'] as String: item['country_name'] as String // }; // countryCodes = countryMap.keys.toList(); // } else { // countryList = []; // countryMap = {}; // countryCodes = []; // } // // setState(() { // isCountryLoading = false; // }); // } Future loadCountryList(newTripType) async { print("πŸ”„ loadCountryList called with tripType: ${newTripType}"); setState(() { isCountryLoading = true; }); final result = await apiService.fetchFlightsCountryList(newTripType); print("ResultCountry : $result"); // Create a map: Country_Code -> "City, Airport" Map tempCountryMap = {}; Map tempCountryCode = {}; for (var country in result) { // String city = country['City'] ?? ''; // String airport = country['Airport'] ?? ''; // String displayName = '${country['City']} - ${country['Airport']}'; final code = country['Code'] ?? ''; final city = country['City'] ?? ''; final airport = country['Airport'] ?? ''; final countyCode = country['Country_Code'] ?? ''; final displayName = '$city ($code)\n$airport'; tempCountryMap[country['Code']] = displayName; tempCountryCode[country['Code']] = countyCode; } setState(() { countryMap = tempCountryMap; // Update the map countryCodeMap = tempCountryCode; isCountryLoading = false; }); } int getRowCount() { if (selectedTripType == "RoundTrip") { return 2; } else if (selectedTripType == "Multitrip") { return multiTripRowCount; } return 1; // Default for Oneway } void loadInitialData() async { String? layoutString = await getLayoutColor(); // setState(() { // layoutColor = // layoutString != null // ? Color(int.parse(layoutString)) // : Colors.redAccent; // }); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; }); } void _initializeFields() { print("_initializeFields-----------------"); // Dispose and clear previous controllers and focus nodes for (var key in List.from(textControllers.keys)) { textControllers[key]?.dispose(); } textControllers.clear(); for (var key in List.from(focusNodes.keys)) { focusNodes[key]?.dispose(); } focusNodes.clear(); textControllers.clear(); focusNodes.clear(); focusStates.clear(); print("Focus Nodes KeysII: ${focusNodes.keys.toList()}"); print("Focus States KeysII: ${focusStates.keys.toList()}"); print("Text Controllers KeysII: ${textControllers.keys.toList()}"); // Determine the row count based on selectedTripType int rowCount = selectedTripType == "Roundtrip" ? 2 : selectedTripType == "Multitrip" ? multiTripRowCount : 1; // Initialize fields dynamically for (var field in dataHeader) { for (int i = 1; i <= rowCount; i++) { textControllers["${field}${i}Controller"] = TextEditingController(); focusNodes["${field}${i}FocusNode"] = FocusNode(); focusStates["${field}${i}Focused"] = false; } } // Add focus listeners after reinitialization for (var key in focusNodes.keys) { _addFocusListener(focusNodes[key]!, (focus) { setState(() { focusStates[key.replaceFirst("FocusNode", "Focused")] = focus; }); }); } setState(() {}); // Ensure UI updates } void addMultiTripRow() { setState(() { multiTripRowCount++; // Increment row count _initializeFields(); // Reinitialize fields with updated count }); } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } void _clearError(String field) { if (mounted && errorMessages.containsKey(field)) { setState(() { errorMessages.remove(field); }); } } @override void dispose() { // _tripTypeFocusNode.dispose(); // Dispose all dynamically created FocusNodes for (var node in focusNodes.values) { node.dispose(); } // Dispose all dynamically created TextEditingControllers for (var controller in textControllers.values) { controller.dispose(); } super.dispose(); } Map get flightsData { List> trips = []; int rowCount = 1; // Default for One-way if (selectedTripType == "Roundtrip") { rowCount = 2; // Fixed count for Roundtrip } else if (selectedTripType == "Multitrip") { rowCount = multiTripRowCount; // Use dynamic count for Multitrip } for (int i = 1; i <= rowCount; i++) { final trip = { "class": selectedClasses[i], "is_this_exceptional": exceptionalClass[i], // "from_place": countryMap[selectedFrom[i]], "from_place": selectedFrom[i], "to_place": selectedTo[i], "from_country_code": countryCodeMap[selectedFrom[i]], "to_country_code": countryCodeMap[selectedTo[i]], // "from_place": textControllers["_from${i}Controller"]?.text ?? "", // "to_place": textControllers["_to${i}Controller"]?.text ?? "", "date": textControllers["_date${i}Controller"]?.text ?? "", "time": textControllers["_time${i}Controller"]?.text ?? "", "created_by": widget.loginUser, "updated_by": widget.loginUser, }; // Check if editing and flight_trip_id exists for this trip // πŸ›  Fix index offset (i - 1) if (widget.selectedItem != null && widget.selectedItem?["trips"] != null && widget.selectedItem!["trips"] is List && (i - 1) < widget.selectedItem!["trips"].length) { final existingTrip = widget.selectedItem!["trips"][i - 1]; if (existingTrip["flight_trip_id"] != null) { trip["flight_trip_id"] = existingTrip["flight_trip_id"]; } } trips.add(trip); // trips.add({ // "class": selectedClasses[i], // "from_place": textControllers["_from${i}Controller"]?.text ?? "", // "to_place": textControllers["_to${i}Controller"]?.text ?? "", // "date": textControllers["_date${i}Controller"]?.text ?? "", // "time": textControllers["_time${i}Controller"]?.text ?? "", // "created_by": widget.loginUser, // "updated_by": widget.loginUser, // }); } Map data = { "trip_type": selectedTripType, "comments": textControllers["_comments1Controller"]?.text ?? "", "visa_available": selectedvisa_available, "created_by": widget.loginUser, "updated_by": widget.loginUser, "trips": trips, }; if (widget.selectedItem != null) { if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; } else if (widget.selectedItem?["flight_id"] != null && widget.selectedItem?["flight_id"] != 0) { data["flight_id"] = widget.selectedItem!["flight_id"]; } } return data; } TextEditingController initController(String key) { return TextEditingController(text: widget.selectedItem?[key] ?? ""); } void handleUpdateField() { if (widget.selectedItem != null) { textControllers["_comments1Controller"] = initController("comments"); // selectedTripType = widget.selectedItem!["trip_type"] as String?; // selectedvisa_available = widget.selectedItem!["visa_available"].toString(); if (widget.selectedItem!["trip_type"] != null) { selectedTripType = widget.selectedItem!["trip_type"].toString(); } if (widget.selectedItem!["visa_available"] != null) { selectedvisa_available = widget.selectedItem!["visa_available"].toString(); } // Extract trips from selectedItem List selectedTrips = widget.selectedItem!["trips"] ?? []; // Ensure the selectedClasses map and textControllers are cleared before populating // selectedClasses.clear(); // textControllers.clear(); // Set row count dynamically for Multitrip if (selectedTripType == "Multitrip") { multiTripRowCount = selectedTrips.length; } // Loop through selected trips and populate text controllers for (int i = 0; i < selectedTrips.length; i++) { var trip = selectedTrips[i] as Map; int index = i + 1; // Use 1-based indexing to match the form selectedClasses[index] = trip["class"].toString(); exceptionalClass[index] = trip["is_this_exceptional"].toString(); selectedFrom[index] = trip["from_place"].toString(); selectedTo[index] = trip["to_place"].toString(); // textControllers["_from${index}Controller"] = // TextEditingController(text: trip["from_place"]); // textControllers["_to${index}Controller"] = // TextEditingController(text: trip["to_place"]); textControllers["_date${index}Controller"] = TextEditingController( text: trip["date"], ); textControllers["_time${index}Controller"] = TextEditingController( text: trip["time"], ); // Check if editing and flight_trip_id exists for this trip if (widget.selectedItem != null && widget.selectedItem?["trips"] != null && widget.selectedItem!["trips"] is List && i < widget.selectedItem!["trips"].length) { final existingTrip = widget.selectedItem!["trips"][i]; if (existingTrip["flight_trip_id"] != null) { trip["flight_trip_id"] = existingTrip["flight_trip_id"]; } } } print("Selected ITEM - ${widget.selectedItem}"); print("Total Trips Loaded: ${selectedTrips.length}"); print("Controllers Set: ${textControllers.keys}"); } } void handleSave() { print("Controllers Key-Value Pairs:"); if (!validateFields()) { print("Validation failed. Please fill all required fields."); return; } widget.onSaveFlight(flightsData); // Send object to parent // widget.onClose(false); widget.onClose("Flight", false); print("FlightData - $flightsData"); } void validateTimeDifference(int index) { if (index <= 1) return; // Skip validation for the first row String? prevDateStr = textControllers["_date${index - 1}Controller"]?.text; String? prevTimeStr = textControllers["_time${index - 1}Controller"]?.text; String? currDateStr = textControllers["_date${index}Controller"]?.text; String? currTimeStr = textControllers["_time${index}Controller"]?.text; print("prevDateTime - $prevTimeStr"); print("currDateTime - $currTimeStr"); if (prevDateStr != null && prevDateStr.isNotEmpty && prevTimeStr != null && prevTimeStr.isNotEmpty && currDateStr != null && currDateStr.isNotEmpty && currTimeStr != null && currTimeStr.isNotEmpty) { try { final format = DateFormat("dd-MM-yyyy HH:mm"); // final prevDateTime = DateTime.parse("$prevDateStr $prevTimeStr"); // final currDateTime = DateTime.parse("$currDateStr $currTimeStr"); final prevDateTime = format.parse("$prevDateStr $prevTimeStr"); final currDateTime = format.parse("$currDateStr $currTimeStr"); if (!currDateTime.isAfter(prevDateTime)) { errorMessages["time_$index"] = "30 mins gap required"; } else if (currDateTime.difference(prevDateTime).inMinutes < 30) { errorMessages["time_$index"] = "30 mins gap required"; } else { errorMessages.remove("time_$index"); } } catch (e) { print(" Invalid time format"); errorMessages["time_$index"] = "Invalid time format"; } } } bool validateFields() { errorMessages.clear(); // Reset errors rowCount = 1; // Default row count for One-way if (selectedTripType == "Roundtrip") { rowCount = 2; // Fixed for Roundtrip } else if (selectedTripType == "Multitrip") { rowCount = multiTripRowCount; // Dynamic row count for Multitrip } // Loop through each trip row and validate required fields for (int i = 1; i <= rowCount; i++) { // if (textControllers["_from${i}Controller"]?.text.trim().isEmpty ?? true) { // errorMessages["from_place_$i"] = "Required"; // } if (selectedFrom[i] == null) { errorMessages["from_place_$i"] = "Required"; } // if (selectedTo[i] == null) { // errorMessages["to_place_$i"] = "Required"; // }else if ( // selectedFrom[i] == selectedTo[i]) { // errorMessages["to_place_$i"] = "Change Destination"; // } if (selectedTo[i] == null) { errorMessages["to_place_$i"] = "Required"; print("Error: to_place_$i -> Required (selectedTo[$i] is null)"); } else if (selectedFrom[i] == selectedTo[i]) { if (selectedTripType == "Roundtrip") { errorMessages["to_place_1"] = "Change Destination"; } else { errorMessages["to_place_$i"] = "Change Destination"; } print( "Error: to_place_$i -> Change Destination (selectedFrom[$i] == selectedTo[$i])", ); } // if (textControllers["_to${i}Controller"]?.text.trim().isEmpty ?? true) { // errorMessages["to_place_$i"] = "Required"; // } // if (textControllers["_date${i}Controller"]?.text.trim().isEmpty ?? true) { // errorMessages["date_$i"] = "Required"; // } if (textControllers["_date${i}Controller"]?.text.trim().isEmpty ?? true) { errorMessages["date_$i"] = "Required"; } if( selectedClasses[i] == null ) { errorMessages["class_$i"] = "Required"; } if (textControllers["_time${i}Controller"]?.text.trim().isEmpty ?? true) { errorMessages["time_$i"] = "Required"; } } //selectedFrom For every trips should n't same Set seenFrom = {}; Set seenTo = {}; if (selectedTripType == "Roundtrip") { for (int i = 1; i <= rowCount; i++) { final fromValue = selectedFrom[i]; final toValue = selectedTo[i]; if (fromValue != null) { if (seenFrom.contains(fromValue)) { errorMessages["from_place_$i"] = "Duplicate Departure "; print("Error: from_place_$i duplicates a previous departure"); } else { seenFrom.add(fromValue); // errorMessages.remove("from_place_$i"); } } if (toValue != null) { if (seenTo.contains(toValue)) { errorMessages["to_place_$i"] = "Duplicate Destination "; print("Error: to_place_$i duplicates a previous departure"); } else { // errorMessages.remove("to_place_$i"); seenTo.add(toValue); } } } } // Step 3: Sequential Date Comparison DateFormat format = DateFormat("dd-MM-yyyy"); // Assumes "12 Jun" format DateTime now = DateTime.now(); List parsedDates = []; for (int i = 1; i <= rowCount; i++) { String? dateStr = textControllers["_date${i}Controller"]?.text.trim(); String? timeStr = textControllers["_time${i}Controller"]?.text.trim(); print("VAlidationDATE - $dateStr "); if (dateStr != null && dateStr.isNotEmpty) { try { DateTime date = format.parseStrict(dateStr); date = DateTime( date.year, date.month, date.day, ); // Assume current year parsedDates.add(date); } catch (e) { errorMessages["date_$i"] = "Invalid format"; } } } for (int i = 1; i < parsedDates.length; i++) { if (parsedDates[i].isAtSameMomentAs(parsedDates[i - 1])) { //Here check time[i] and time[i-1] if same error 30 mins gap reeuired print("Ckecking Same DAte"); // Same date - check time gap // Combine parsedDates and times into DateTime objects String prevDateStr = textControllers["_date${i}Controller"]!.text.trim(); String prevTimeStr = textControllers["_time${i}Controller"]!.text.trim(); String currDateStr = textControllers["_date${i + 1}Controller"]!.text.trim(); String currTimeStr = textControllers["_time${i + 1}Controller"]!.text.trim(); final dtFormat = DateFormat("dd-MM-yyyy HH:mm"); final prevDT = dtFormat.parse("$prevDateStr $prevTimeStr"); final currDT = dtFormat.parse("$currDateStr $currTimeStr"); int diffMins = currDT.difference(prevDT).inMinutes; print("Time difference between row ${i} and ${i + 1}: $diffMins mins"); // If same day (diff >= 0 & < 1440 minutes), enforce 30‑min minimum gap if (diffMins < 0) { errorMessages["time_${i + 1}"] = "Must be after previous"; } else if (diffMins < 30) { errorMessages["time_${i + 1}"] = "30 mins gap required"; } else { errorMessages.remove("time_${i + 1}"); } print("Ckecking Same DAte...1"); } else if (!parsedDates[i].isAfter(parsedDates[i - 1])) { errorMessages["date_${i + 1}"] = "Must be after date_${i}"; print("Error: date_${i + 1} is not after date_${i}"); } else { errorMessages.remove("date_${i + 1}"); } } setState(() {}); // Update UI to show error messages return errorMessages .isEmpty; // Returns true if all required fields are filled } void removeTrip(int index) { if (selectedTripType == "Multitrip" && multiTripRowCount > 1) { print("Delete Index - $index"); setState(() { multiTripRowCount--; // Reduce trip count // Remove corresponding text controllers textControllers.remove("_from${index}Controller"); textControllers.remove("_to${index}Controller"); textControllers.remove("_date${index}Controller"); textControllers.remove("_time${index}Controller"); // Step 2: Shift remaining textControllers keys Map updatedTextControllers = {}; int newIndex = 1; for (int i = 1; i <= multiTripRowCount + 1; i++) { if (i == index) continue; // Skip the deleted one // updatedTextControllers["_from${newIndex}Controller"] = // textControllers["_from${i}Controller"]!; updatedTextControllers["_to${newIndex}Controller"] = textControllers["_to${i}Controller"]!; updatedTextControllers["_date${newIndex}Controller"] = textControllers["_date${i}Controller"]!; updatedTextControllers["_time${newIndex}Controller"] = textControllers["_time${i}Controller"]!; newIndex++; } textControllers = updatedTextControllers; // Shift the selectedClasses map BEFORE removing the index Map updatedClasses = {}; newIndex = 1; for (int i = 1; i <= selectedClasses.length; i++) { if (i == index) continue; // Skip the one being deleted updatedClasses[newIndex] = selectedClasses[i]; newIndex++; } selectedClasses = updatedClasses; // Update the map Map updatedExptionalClasses = {}; newIndex = 1; for (int i = 1; i <= exceptionalClass.length; i++) { if (i == index) continue; // Skip the one being deleted updatedExptionalClasses[newIndex] = exceptionalClass[i]; newIndex++; } exceptionalClass = updatedExptionalClasses; // Shift the selectedFrom map BEFORE removing the index Map updatedFrom = {}; newIndex = 1; for (int i = 1; i <= selectedFrom.length; i++) { if (i == index) continue; // Skip the one being deleted updatedFrom[newIndex] = selectedFrom[i]; newIndex++; } selectedFrom = updatedFrom; // Shift the selectedTo map BEFORE removing the index Map updatedTo = {}; newIndex = 1; for (int i = 1; i <= selectedTo.length; i++) { if (i == index) continue; // Skip the one being deleted updatedTo[newIndex] = selectedTo[i]; newIndex++; } selectedTo = updatedTo; print("FlightData - $flightsData"); print("Updated Trips: $multiTripRowCount"); print("Updated Classes: $selectedClasses"); }); } } @override Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, sizingInfo) { bool isMobile = sizingInfo.isMobile; bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Container( // color: Color(0xFFF4F4FB), // color: Color(0xFFF9F9F9), // Slightly lighter than white child: Form( key: _formKey, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Padding( padding: const EdgeInsets.only(top: 30.0), child: Center( child: Column(children: _buildAccomadtionForm(isDesktop)), ), ), ], ), ), ), ); }, ); } List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), SizedBox(height: 10), ]; } List> rowBuilders = [ // _builClassType(isDesktop, 1), _buildSecondRow(isDesktop, 1), ]; List> rowRoundBuilders = [ // _builClassType(isDesktop, 1), _buildSecondRow(isDesktop, 1), // _builClassType(isDesktop, 2), _buildSecondRow(isDesktop, 2), ]; print("Trip Type Selected: $selectedTripType"); return [ ...buildResponsiveRow(_buildFirstRow(isDesktop)), // Iterate over rowBuilders based on selectedTripType if (selectedTripType == "Oneway") ...rowBuilders.expand((row) => buildResponsiveRow(row)), if (selectedTripType == "Roundtrip") ...rowRoundBuilders.expand((row) => buildResponsiveRow(row)), if (selectedTripType == "Multitrip") ...List.generate(multiTripRowCount, (index) { // List firstRow = _builClassType(isDesktop, index + 1); List secondRow = _buildSecondRow(isDesktop, index + 1); return [ // ...buildResponsiveRow(firstRow), // Row 1 ...buildResponsiveRow(secondRow), // Row 2 ]; }).expand((row) => row), // if (selectedTripType == "Multitrip") // ...List.generate(multiTripRowCount, (index) => // buildResponsiveRow(_builClassType(isDesktop, index + 1) + _buildSecondRow(isDesktop, index + 1)) // ).expand((row) => row), if (selectedTripType == "Multitrip") Align( alignment: Alignment.centerRight, child: ElevatedButton( style: ElevatedButton.styleFrom( minimumSize: !isDesktop ? const Size.fromHeight(48) : null, // No min height on desktop // height backgroundColor: Colors.green, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), onPressed: () { setState(() { multiTripRowCount++; // Increase the row count }); // Dynamically create controllers and focus nodes for new trip fields for (var field in dataHeader) { String keyController = "${field}${multiTripRowCount}Controller"; String keyFocusNode = "${field}${multiTripRowCount}FocusNode"; String keyFocusState = "${field}${multiTripRowCount}Focused"; // Create TextEditingController if it doesn't exist if (!textControllers.containsKey(keyController)) { textControllers[keyController] = TextEditingController(); } // Create FocusNode if it doesn't exist if (!focusNodes.containsKey(keyFocusNode)) { focusNodes[keyFocusNode] = FocusNode(); // Attach focus listener for dynamic fields focusNodes[keyFocusNode]!.addListener(() { setState(() { focusStates[keyFocusState] = focusNodes[keyFocusNode]!.hasFocus; }); }); } // Initialize focus state focusStates[keyFocusState] = false; } // _initializeFields(); }, child: Text( "Add Trip", style: TextStyle(color: Colors.white, fontSize: 12), ), ), ), // ...buildResponsiveRow(_buildvisa(isDesktop)), ...buildResponsiveRow(_buildThirdRow(isDesktop)), // Actions row remains a Row ]; } List filterAndSortCountryList(List items, String filter) { final lowerFilter = filter.toLowerCase(); int getMatchScore(String item) { final parts = item.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1].toLowerCase() : ''; final match = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode); final city = match?.group(1)?.toLowerCase() ?? ''; final code = match?.group(2)?.toLowerCase() ?? ''; if (code == lowerFilter) return 0; if (code.startsWith(lowerFilter)) return 1; if (code.contains(lowerFilter)) return 2; if (city == lowerFilter) return 3; if (city.startsWith(lowerFilter)) return 4; if (city.contains(lowerFilter)) return 5; if (airport.contains(lowerFilter)) return 6; return 999; } return items.where((item) { final parts = item.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1].toLowerCase() : ''; final match = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode); final city = match?.group(1)?.toLowerCase() ?? ''; final code = match?.group(2)?.toLowerCase() ?? ''; return code.contains(lowerFilter) || city.contains(lowerFilter) || airport.contains(lowerFilter); }).toList() ..sort((a, b) => getMatchScore(a).compareTo(getMatchScore(b))); } List _buildFirstRow(isDesktop) { return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Trip Type", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), isDesktop ? Row(children: _buildTripType(isDesktop)) : Column(children: _buildTripType(isDesktop)), ], ), if (isDesktop) Spacer() else SizedBox(height: 8), ]; } List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['flight_trip_type'] ?? []; List> dropdownItems = purposeList .map( (item) => DropdownMenuItem( value: item['dropdown_value'], child: Text(item['dropdown_value']), ), ) .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text( "No options available", style: TextStyle(color: Colors.grey), ), ), ); } return [ CustomTextFieldWrapper( // isFocused: _tripTypeFocused, // isFocused: focusStates["_tripType1Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: focusNodes["_tripType1FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_tripType1Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_tripType1FocusNode"]?.requestFocus(); }, child: DropdownSearch( items: purposeList .map((item) => item['dropdown_value'] as String) .toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_tripType1Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_tripType1Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 1), ), contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), onChanged: (newValue) { setState(() { selectedTripType = newValue; if (selectedTripType != "Multitrip") { multiTripRowCount = 1; } errorMessages.clear(); for (int i = 1; i <= rowCount; i++) { textControllers["_date${i}Controller"]?.clear(); textControllers["_time${i}Controller"]?.clear(); // Also clear the actual selected data (not just the text in controllers) selectedFrom[i] = null; // Assuming null means no selection selectedTo[i] = null; } }); print( "Updating form data: Flight -> trip_type -> $selectedTripType", ); _initializeFields(); }, selectedItem: selectedTripType, dropdownBuilder: (context, selectedItem) => Align( alignment: Alignment.centerLeft, child: Text( selectedItem ?? "Select", style: TextStyle(fontSize: 12), ), ), popupProps: PopupProps.menu( constraints: BoxConstraints(maxHeight: 100), menuProps: MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) => Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 6.0, ), child: Text( item, style: TextStyle( fontSize: 13, ), // Custom text size for dropdown items ), ), ), ), ), ), // DropdownButtonFormField( // isExpanded: true, // // focusNode: _tripTypeFocusNode, // Assign the correct focus node // focusNode: focusNodes["_tripType1FocusNode"], // value: selectedTripType, // style: TextStyle(fontSize: 12), // isDense: true, // dropdownColor: Colors.white, // decoration: InputDecoration( // border: InputBorder.none, // contentPadding: // EdgeInsets.symmetric(horizontal: 10), // Proper padding // ), // onChanged: purposeList.isNotEmpty // ? (newValue) { // setState(() { // selectedTripType = newValue; // // selectedTripType = "Oneway"; // // Reset `multiTripRowCount` when switching away from Multitrip // if (selectedTripType != "Multitrip") { // multiTripRowCount = 1; // } // errorMessages.clear(); // }); // print( // "Updating form data: Flight -> trip_type -> $selectedTripType"); // _initializeFields(); // // // _initializeRows(); // } // : null, // // items: dropdownItems, // ), ), ), ]; } // Widget _buildDelete(bool isDesktop, int index) { // return Container( // color: Colors.blueAccent, // child: Row( // mainAxisAlignment: MainAxisAlignment.start, // children: [ // Text( // "Trip ${index}", // style: TextStyle( // fontSize: 14, // fontWeight: FontWeight.w600, // color: Color(0xFF575A74), // ), // ), // SizedBox( // width: isDesktop // ? MediaQuery.of(context).size.width * 0.38 // : MediaQuery.of(context).size.width * 0.3, // child: Stack( // alignment: Alignment.center, // Centers the icon // children: [ // Divider( // color: Color(0xFF8B8FB2), // thickness: 0.5, // height: 20, // ), // Container( // // padding: EdgeInsets.all(4), // color: Colors.white, // Background to avoid overlapping // child: Row( // mainAxisSize: // MainAxisSize.min, // Prevents row from taking full width // children: [ // Icon(Icons.add_circle_sharp, // color: Colors.blue, size: 28), // ], // ), // ), // ], // ), // ), // IconButton( // onPressed: () { // removeTrip(index); // }, // icon: Icon(Icons.delete), // color: Colors.red, // iconSize: 20, // ) // ], // ), // ); // } List _buildDelete(bool isDesktop, int index) { return [ Container( padding: const EdgeInsets.all(10), // padding: const EdgeInsets.only(left: 10, right: 10), // color: Colors.white, child: Text( "Trip ${index}", style: TextStyle( fontSize: 14, color: Colors.blueAccent, fontWeight: FontWeight.w600, ), ), ), SizedBox( width: isDesktop ? MediaQuery.of(context).size.width * 0.58 : 80, // Ensure full width child: Stack( alignment: Alignment.center, // Centers the icon children: [ Divider(color: Color(0xFF8B8FB2), thickness: 0.5, height: 20), Container( // padding: EdgeInsets.all(4), color: Colors.white, // Background to avoid overlapping child: Row( mainAxisSize: MainAxisSize.min, // Prevents row from taking full width children: [ Icon(Icons.add_circle_sharp, color: Colors.blue, size: 28), ], ), ), ], ), ), // SizedBox( // width: isDesktop // ? MediaQuery.of(context).size.width * 0.29 // : MediaQuery.of(context).size.width * 0.3, // child: Stack( // alignment: Alignment.center, // Centers the icon // children: [ // Divider( // color: Color(0xFF8B8FB2), // thickness: 0.5, // height: 20, // ), // Container( // // padding: EdgeInsets.all(4), // color: Colors.white, // Background to avoid overlapping // child: Row( // mainAxisSize: // MainAxisSize.min, // Prevents row from taking full width // children: [ // Icon(Icons.add_circle_sharp, color: Colors.blue, size: 28), // ], // ), // ), // ], // ), // ), Container( // color: Colors.white, // padding: const EdgeInsets.only(left: 10, right: 10), child: IconButton( onPressed: () { removeTrip(index); }, icon: Icon(Icons.delete), color: Colors.blueAccent, iconSize: 20, ), ), ]; } List _buildSecondRow(bool isDesktop, int index) { bool isFlightClassLoading = widget.apiDataForClass?['flight_class'] == null; List purposeList = widget.apiDataForClass?['flight_class'] ?? []; 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 // selectedClasses[index] ??= // dropdownItems.isNotEmpty ? dropdownItems.first.value : null; // ------------------------------------------------- DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; Future _selectCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); // Determine the minimum date (firstDate) based on previous index if available DateTime firstDate = today; if (index == 1 && flightLastTripDateNotifier.value != null && flightLastTripDateNotifier.value!.isNotEmpty) { try { final tripDate = DateFormat( 'dd-MM-yyyy', ).parseStrict(flightLastTripDateNotifier.value!); if (tripDate.isAfter(today)) { firstDate = tripDate; } } catch (_) { // handle parse error if needed } } else if (index > 1) { final previousDateString = textControllers["_date${index - 1}Controller"]?.text; if (previousDateString != null && previousDateString.isNotEmpty) { try { final previousDate = DateFormat( 'dd-MM-yyyy', ).parseStrict(previousDateString); if (previousDate.isAfter(today)) { firstDate = previousDate; } } catch (_) { // handle parse error if necessary } } } DateTime initialDate = _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(firstDate) ? _selectedCheckOutDate! : firstDate; DateTime? pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: firstDate, lastDate: DateTime(2100), initialEntryMode: DatePickerEntryMode.calendarOnly, ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); textControllers["_date${index}Controller"]?.text = DateFormat( 'dd-MM-yyyy', ).format(pickedDate); }); } } Future _selectCheckOutTime( BuildContext context, int index, VoidCallback onPicked, ) async { TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), ); if (pickedTime != null) { final now = DateTime.now(); // Parse the selected date final dateText = textControllers["_date${index}Controller"]?.text ?? ""; final selectedDate = DateFormat( 'dd-MM-yyyy', ).parse(dateText); // or 'yyyy-MM-dd' depending on your format final selectedDateTime = DateTime( selectedDate.year, selectedDate.month, selectedDate.day, pickedTime.hour, pickedTime.minute, ); // βœ… Only validate past time if date is today final isToday = selectedDate.year == now.year && selectedDate.month == now.month && selectedDate.day == now.day; bool isPastTime = selectedDateTime.isBefore(now); if (isToday && isPastTime) { setState(() { errorMessages["time_$index"] = "You can't select a past time."; }); return; } // βœ… Valid time selection setState(() { _selectedCheckOutTime = pickedTime; final formattedTime = DateFormat('HH:mm').format(selectedDateTime); textControllers["_time${index}Controller"]?.text = formattedTime; // errorMessages["time_$index"] = ""; // clear previous error errorMessages.remove("time_$index"); onPicked(); // Trigger callback }); } // if (pickedTime != null && pickedTime != _selectedCheckOutTime) { // setState(() { // _selectedCheckOutTime = pickedTime; // // Formatting time to HH:mm (24-hour format) // final now = DateTime.now(); // final formattedTime = DateFormat('HH:mm').format( // DateTime( // now.year, // now.month, // now.day, // pickedTime.hour, // pickedTime.minute, // ), // ); // // // _timeController.text = formattedTime; // textControllers["_time${index}Controller"]?.text = formattedTime; // // onPicked(); // }); // } } return isDesktop ? [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "From*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: _fromFocus, // isFocused: focusStates["_from${index}Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: isCountryLoading ? Center(child: CircularProgressIndicator()) : Focus( focusNode: focusNodes["_from${index}FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_from${index}Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_from${index}FocusNode"] ?.requestFocus(); }, child: DropdownSearch( enabled: !(selectedTripType == "Roundtrip" && index == 2), selectedItem: selectedFrom[index] != null ? countryMap[selectedFrom[index]] : null, items: countryMap.values.toList(), // asyncItems: (String filter) async { // final allItems = countryMap.values.toSet().toList(); // print("Original items: ${allItems.length}"); // final filtered = filterAndSortCountryList(allItems, filter); // // print("Filtered items: ${filtered.length} -> $filtered"); // return filtered; // }, popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search...", contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 1, ), ), style: TextStyle(fontSize: 12), ), menuProps: MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) { final parts = item.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; // Extract city and code from "City (CODE)" final cityMatch = RegExp( r'^(.*)\s+\(([^)]+)\)$', ).firstMatch(cityAndCode); final city = cityMatch?.group(1) ?? ''; final code = cityMatch?.group(2) ?? ''; return Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 6.0, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( city, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), Text( code, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 1), Text( airport, style: const TextStyle( fontSize: 11, color: Colors.grey, overflow: TextOverflow.ellipsis, ), ), ], ), ); }, // itemBuilder: // (context, item, isSelected) => Padding( // padding: const EdgeInsets.symmetric( // horizontal: 8.0, // vertical: 6.0, // ), // child: Text( // item, // style: TextStyle( // fontSize: 13, // ), // πŸ‘ˆ Set your desired text size here // ), // ), ), // items: countryMap.values.toList(), // filterFn: (item, filter) { // final lowerFilter = filter.toLowerCase(); // // final parts = item.split('\n'); // final cityAndCode = parts[0]; // final airport = parts.length > 1 ? parts[1].toLowerCase() : ''; // // // Extract city and code from "City (CODE)" // final cityMatch = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode); // final city = cityMatch?.group(1)?.toLowerCase() ?? ''; // final code = cityMatch?.group(2)?.toLowerCase() ?? ''; // // // Priority: Code > City > Airport // return code.contains(lowerFilter) || // city.contains(lowerFilter) || // airport.contains(lowerFilter); // }, dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_from${index}Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_from${index}Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: layoutColor, width: 1, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null) { return Align( alignment: Alignment.centerLeft, child: const Text( "Select", style: TextStyle(fontSize: 12), ), ); } final parts = selectedItem.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; return Align( alignment: Alignment.centerLeft, child: Text( cityAndCode ?? "Select", style: const TextStyle(fontSize: 12), ), ); }, // dropdownBuilder: // (context, selectedItem) => Align( // alignment: Alignment.centerLeft, // child: Text( // selectedItem ?? "Select", // style: TextStyle(fontSize: 12), // ), // ), onChanged: (String? newValue) { setState(() { errorMessages.remove("from_place_$index"); // selectedFrom[index] = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; selectedFrom[index] = countryMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; if (selectedTripType == "Roundtrip") { print('rounfTo${selectedFrom[index]}'); // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; selectedTo[2] = selectedFrom[index]!; } }); }, ), ), ), ), // child: SizedBox( // height: 40, // child: TextField( // // focusNode: _fromFocusNode, // focusNode: focusNodes["_from${index}FocusNode"], // controller: textControllers["_from${index}Controller"], // style: const TextStyle(fontSize: 12), // decoration: const InputDecoration( // labelText: "From", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 16), // ), // ), // ), ), if (errorMessages["from_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["from_place_$index"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) SizedBox(width: 20) else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "To*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: focusStates["_to${index}Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, child: isCountryLoading ? Center(child: CircularProgressIndicator()) : Focus( focusNode: focusNodes["_to${index}FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_to${index}Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_to${index}FocusNode"]?.requestFocus(); }, child: DropdownSearch( enabled: !(selectedTripType == "Roundtrip" && index == 2), selectedItem: selectedTo[index] != null ? countryMap[selectedTo[index]] : null, popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search...", contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 1, ), ), style: TextStyle(fontSize: 12), ), menuProps: MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) { final parts = item.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; // Extract city and code from "City (CODE)" final cityMatch = RegExp( r'^(.*)\s+\(([^)]+)\)$', ).firstMatch(cityAndCode); final city = cityMatch?.group(1) ?? ''; final code = cityMatch?.group(2) ?? ''; return Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 6.0, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( city, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), Text( code, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 1), Text( airport, style: const TextStyle( fontSize: 11, color: Colors.grey, overflow: TextOverflow.ellipsis, ), ), ], ), ); }, // itemBuilder: // (context, item, isSelected) => Padding( // padding: const EdgeInsets.symmetric( // horizontal: 8.0, // vertical: 6.0, // ), // child: Text( // item, // style: TextStyle( // fontSize: 13, // ), // πŸ‘ˆ Set your desired text size here // ), // ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_to${index}Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_to${index}Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: layoutColor, width: 1, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), // dropdownBuilder: // (context, selectedItem) => Align( // alignment: Alignment.centerLeft, // child: Text( // selectedItem ?? "Select Country", // style: TextStyle(fontSize: 12), // ), // ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null) { return Align( alignment: Alignment.centerLeft, child: const Text( "Select", style: TextStyle(fontSize: 12), ), ); } final parts = selectedItem.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; return Align( alignment: Alignment.centerLeft, child: Text( cityAndCode ?? "Select", style: const TextStyle(fontSize: 12), ), ); }, onChanged: (String? newValue) { setState(() { errorMessages.remove("to_place_$index"); selectedTo[index] = countryMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; print(selectedTo[index]); if (selectedTripType == "Roundtrip") { print('rounfTo${selectedTo[index]}'); // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; selectedFrom[2] = selectedTo[index]!; } }); }, ), ), ), ), ), if (errorMessages["to_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["to_place_$index"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text( // "Class *", // style: GoogleFonts.poppins( // fontSize: 12, // fontWeight: FontWeight.w600, // color: Color(0xFF575A74), // ), // ), // SizedBox(height: 5), // CustomTextFieldItnerarySubWrapper( // isFocused: focusStates["_class${index}Focused"] ?? false, // isDesktop: isDesktop, // // width: isDesktop // // ? MediaQuery.of(context).size.width * 0.34 // // : MediaQuery.of(context).size.width * 0.66, // child: SizedBox( // height: 40, // child: // isFlightClassLoading // ? const Center(child: CircularProgressIndicator()) // : DropdownButtonFormField( // focusNode: focusNodes["_class${index}FocusNode"], // // focusNode: _tripTypeFocusNode, // Assign the correct focus node // // controller: _hotelNameController, // value: selectedClasses[index], // // style: TextStyle(fontSize: 12), // decoration: InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric( // horizontal: 10, // ), // Proper padding // ), // onChanged: // purposeList.isNotEmpty // ? (newValue) { // setState(() { // selectedClasses[index] = newValue; // }); // // print(selectedClasses[index]); // } // : null, // items: dropdownItems, // ), // ), // ), // ], // ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Class *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: focusStates["_class${index}Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: focusNodes["_class${index}FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_class${index}Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_class${index}FocusNode"]?.requestFocus(); }, child: DropdownSearch>( items: purposeList.cast>(), key: selectedClasses[index] == null ? UniqueKey() : ValueKey(selectedClasses[index]), // selectedItem: purposeList.firstWhere( // (item) => item['dropdown_key'] == selectedClasses[index], // orElse: () => {}, // ), selectedItem: selectedClasses[index] != null ? purposeList.firstWhere( (item) => item['dropdown_key'] == selectedClasses[index], orElse: () => {}, ) : null, itemAsString: (item) => item['dropdown_value'] ?? '', popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, menuProps: const MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) { final bool isNotAllowed = item['is_allowed'] == 'No'; return Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 8, ), child: Text( item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: isNotAllowed ? Colors.redAccent : Colors.black, ), ), ); }, ), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_class${index}Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_class${index}Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 1), ), contentPadding: const EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( "Select Class", style: GoogleFonts.poppins( color: Colors.grey, fontSize: 13, ), ); } return Text( selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ); }, onChanged: purposeList.isNotEmpty ? (Map? newValue) async { // if (newValue != null && // newValue['is_allowed'] == 'No') { // print('not allowed'); // // exceptionalClass[index] = "1"; // } else if (newValue != null && // newValue['is_allowed'] == 'yes') { // print('not allowed'); // // exceptionalClass[index] = "0"; // } // // setState(() { // selectedClasses[index] = // newValue?['dropdown_key']; // print( // "Selected Class: ${selectedClasses[index]}", // ); // }); if (newValue != null && newValue['is_allowed'] == 'No') { final confirm = await showNotAllowedDialog( context, message: "You are not entitled for this service. If you wish to continue with this service, you may require another approval.", ); if (confirm) { setState(() { exceptionalClass[index] = "1"; selectedClasses[index] = newValue['dropdown_key']; }); print( "Selected Purpose: ${selectedClasses[index]}", ); } else { setState(() { exceptionalClass[index] = "0"; selectedClasses[index] = null; // ❌ Clear selection if user cancels }); print( "selectedItem resolved to: ${selectedClasses[index]}", ); } return; } else if (newValue != null && newValue['is_allowed'] == 'yes') { setState(() { exceptionalClass[index] = "0"; selectedClasses[index] = newValue?['dropdown_key']; print( "Selected Purpose: ${selectedClasses[index]}", ); }); } } : null, ), ), ), ), ), if (errorMessages["class_$index"] != null) Padding( padding: const EdgeInsets.only(top: 4), child: Text( errorMessages["class_$index"]!, style: GoogleFonts.poppins(fontSize: 12, color: Colors.red), ), ), ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Date*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_date${index}Focused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: () { focusNodes["_date${index}FocusNode"]?.requestFocus(); _selectCheckOutDate(context); }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["_date${index}FocusNode"], // controller: _dateController, controller: textControllers["_date${index}Controller"], readOnly: true, style: const TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Select Date", labelStyle: const TextStyle( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), // border: OutlineInputBorder( // borderRadius: BorderRadius.circular(8), // borderSide: BorderSide( // color: // (focusStates["_date${index}Focused"] ?? false) // ? layoutColor! // : Colors.white, // width: 0.5, // ), // ), // enabledBorder: OutlineInputBorder( // borderSide: BorderSide( // color: // (focusStates["_date${index}Focused"] ?? false) // ? layoutColor // : Colors.white, // // : const Color(0xFFD6D5E6), // width: 0.5, // // const Color(0xFFD6D5E6), // ), // ), // focusedBorder: OutlineInputBorder( // borderSide: BorderSide(color: layoutColor, width: 1), // ), // contentPadding: const EdgeInsets.symmetric( // horizontal: 10, // vertical: 5, // ), suffixIcon: const Icon( Icons.calendar_today, size: 16, color: Colors.grey, ), ), ), ), ), ), ), if (errorMessages["date_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["date_$index"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Time*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_time${index}Focused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: () { focusNodes["_time${index}FocusNode"]?.requestFocus(); textControllers["_time${index}Controller"]?.text = ""; _clearError("time_$index"); // validateTimeDifference(index); // _selectCheckOutTime(context); _selectCheckOutTime(context, index, () { validateTimeDifference(index); setState( () {}, ); // βœ… Force rebuild to show the error immediately }); }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["_time${index}FocusNode"], // controller: _timeController, controller: textControllers["_time${index}Controller"], readOnly: true, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Time", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon( Icons.access_time, size: 16, color: Colors.grey, ), ), ), ), // Focus( // focusNode: focusNodes["_time${index}FocusNode"], // onFocusChange: (hasFocus) { // setState(() { // focusStates["_time${index}Focused"] = hasFocus; // }); // }, // child: GestureDetector( // // // onTap: () { // // Request focus when user taps // focusNodes["_time${index}FocusNode"]?.requestFocus(); // }, ), ), ), if (errorMessages["time_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["time_$index"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (selectedTripType == "Multitrip") Container( // color: Colors.blueGrey, // padding: const EdgeInsets.only(top: 50, bottom: 50), child: IconButton( onPressed: () { removeTrip(index); }, icon: Icon(Icons.close, color: Colors.redAccent, size: 20), ), ), ] : [ // For mobile, wrap inside a card with padding and grey background Card( elevation: 1, margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), color: const Color(0xFFF5F5F5), // Light grey shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "From*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: _fromFocus, // isFocused: focusStates["_from${index}Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, isDesktop: isDesktop, child: SizedBox( height: 40, child: isCountryLoading ? Center(child: CircularProgressIndicator()) : Focus( focusNode: focusNodes["_from${index}FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_from${index}Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_from${index}FocusNode"] ?.requestFocus(); }, child: DropdownSearch( enabled: !(selectedTripType == "Roundtrip" && index == 2), selectedItem: selectedFrom[index] != null ? countryMap[selectedFrom[index]] : null, items: countryMap.values.toList(), // asyncItems: (String filter) async { // final allItems = countryMap.values.toSet().toList(); // print("Original items: ${allItems.length}"); // final filtered = filterAndSortCountryList(allItems, filter); // // print("Filtered items: ${filtered.length} -> $filtered"); // return filtered; // }, popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search...", contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 1, ), ), style: TextStyle(fontSize: 12), ), menuProps: MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) { final parts = item.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; // Extract city and code from "City (CODE)" final cityMatch = RegExp( r'^(.*)\s+\(([^)]+)\)$', ).firstMatch(cityAndCode); final city = cityMatch?.group(1) ?? ''; final code = cityMatch?.group(2) ?? ''; return Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 6.0, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( city, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), Text( code, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 1), Text( airport, style: const TextStyle( fontSize: 11, color: Colors.grey, overflow: TextOverflow.ellipsis, ), ), ], ), ); }, // itemBuilder: // (context, item, isSelected) => Padding( // padding: const EdgeInsets.symmetric( // horizontal: 8.0, // vertical: 6.0, // ), // child: Text( // item, // style: TextStyle( // fontSize: 13, // ), // πŸ‘ˆ Set your desired text size here // ), // ), ), // items: countryMap.values.toList(), // filterFn: (item, filter) { // final lowerFilter = filter.toLowerCase(); // // final parts = item.split('\n'); // final cityAndCode = parts[0]; // final airport = parts.length > 1 ? parts[1].toLowerCase() : ''; // // // Extract city and code from "City (CODE)" // final cityMatch = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode); // final city = cityMatch?.group(1)?.toLowerCase() ?? ''; // final code = cityMatch?.group(2)?.toLowerCase() ?? ''; // // // Priority: Code > City > Airport // return code.contains(lowerFilter) || // city.contains(lowerFilter) || // airport.contains(lowerFilter); // }, dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_from${index}Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_from${index}Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: layoutColor, width: 1, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null) { return Align( alignment: Alignment.centerLeft, child: const Text( "Select", style: TextStyle(fontSize: 12), ), ); } final parts = selectedItem.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; return Align( alignment: Alignment.centerLeft, child: Text( cityAndCode ?? "Select", style: const TextStyle(fontSize: 12), ), ); }, // dropdownBuilder: // (context, selectedItem) => Align( // alignment: Alignment.centerLeft, // child: Text( // selectedItem ?? "Select", // style: TextStyle(fontSize: 12), // ), // ), onChanged: (String? newValue) { setState(() { errorMessages.remove("from_place_$index"); // selectedFrom[index] = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; selectedFrom[index] = countryMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; if (selectedTripType == "Roundtrip") { print('rounfTo${selectedFrom[index]}'); // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; selectedTo[2] = selectedFrom[index]!; } }); }, ), ), ), ), // child: SizedBox( // height: 40, // child: TextField( // // focusNode: _fromFocusNode, // focusNode: focusNodes["_from${index}FocusNode"], // controller: textControllers["_from${index}Controller"], // style: const TextStyle(fontSize: 12), // decoration: const InputDecoration( // labelText: "From", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 16), // ), // ), // ), ), if (errorMessages["from_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["from_place_$index"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) SizedBox(width: 20) else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "To*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: focusStates["_to${index}Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, child: isCountryLoading ? Center(child: CircularProgressIndicator()) : Focus( focusNode: focusNodes["_to${index}FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_to${index}Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_to${index}FocusNode"]?.requestFocus(); }, child: DropdownSearch( enabled: !(selectedTripType == "Roundtrip" && index == 2), selectedItem: selectedTo[index] != null ? countryMap[selectedTo[index]] : null, popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 220), showSearchBox: true, // Enables search functionality searchFieldProps: TextFieldProps( decoration: InputDecoration( hintText: "Search...", contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 1, ), ), style: TextStyle(fontSize: 12), ), menuProps: MenuProps( backgroundColor: Colors.white, ), itemBuilder: (context, item, isSelected) { final parts = item.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; // Extract city and code from "City (CODE)" final cityMatch = RegExp( r'^(.*)\s+\(([^)]+)\)$', ).firstMatch(cityAndCode); final city = cityMatch?.group(1) ?? ''; final code = cityMatch?.group(2) ?? ''; return Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 6.0, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( city, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), Text( code, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold, ), ), ], ), const SizedBox(height: 1), Text( airport, style: const TextStyle( fontSize: 11, color: Colors.grey, overflow: TextOverflow.ellipsis, ), ), ], ), ); }, // itemBuilder: // (context, item, isSelected) => Padding( // padding: const EdgeInsets.symmetric( // horizontal: 8.0, // vertical: 6.0, // ), // child: Text( // item, // style: TextStyle( // fontSize: 13, // ), // πŸ‘ˆ Set your desired text size here // ), // ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_to${index}Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_to${index}Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: layoutColor, width: 1, ), ), contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), // dropdownBuilder: // (context, selectedItem) => Align( // alignment: Alignment.centerLeft, // child: Text( // selectedItem ?? "Select Country", // style: TextStyle(fontSize: 12), // ), // ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null) { return Align( alignment: Alignment.centerLeft, child: const Text( "Select", style: TextStyle(fontSize: 12), ), ); } final parts = selectedItem.split('\n'); final cityAndCode = parts[0]; final airport = parts.length > 1 ? parts[1] : ''; return Align( alignment: Alignment.centerLeft, child: Text( cityAndCode ?? "Select", style: const TextStyle(fontSize: 12), ), ); }, onChanged: (String? newValue) { setState(() { errorMessages.remove("to_place_$index"); selectedTo[index] = countryMap.entries .firstWhere( (entry) => entry.value == newValue, ) .key; print(selectedTo[index]); if (selectedTripType == "Roundtrip") { print('rounfTo${selectedTo[index]}'); // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; selectedFrom[2] = selectedTo[index]!; } }); }, ), ), ), ), ), if (errorMessages["to_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["to_place_$index"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Class *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: focusStates["_class${index}Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 40, width: double.infinity, child: Focus( focusNode: focusNodes["_class${index}FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_class${index}Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_class${index}FocusNode"]?.requestFocus(); }, child: DropdownSearch>( items: purposeList.cast>(), key: selectedClasses[index] == null ? UniqueKey() : ValueKey(selectedClasses[index]), // selectedItem: purposeList.firstWhere( // (item) => item['dropdown_key'] == selectedClasses[index], // orElse: () => {}, // ), selectedItem: selectedClasses[index] != null ? purposeList.firstWhere( (item) => item['dropdown_key'] == selectedClasses[index], orElse: () => {}, ) : null, itemAsString: (item) => item['dropdown_value'] ?? '', popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, menuProps: const MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) { final bool isNotAllowed = item['is_allowed'] == 'No'; return Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 8, ), child: Text( item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: isNotAllowed ? Colors.redAccent : Colors.black, ), ), ); }, ), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_class${index}Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_class${index}Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 1), ), contentPadding: const EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( "Select Class", style: GoogleFonts.poppins( color: Colors.grey, fontSize: 13, ), ); } return Text( selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ); }, onChanged: purposeList.isNotEmpty ? (Map? newValue) async { // if (newValue != null && // newValue['is_allowed'] == 'No') { // print('not allowed'); // // exceptionalClass[index] = "1"; // } else if (newValue != null && // newValue['is_allowed'] == 'yes') { // print('not allowed'); // // exceptionalClass[index] = "0"; // } // // setState(() { // selectedClasses[index] = // newValue?['dropdown_key']; // print( // "Selected Class: ${selectedClasses[index]}", // ); // }); if (newValue != null && newValue['is_allowed'] == 'No') { final confirm = await showNotAllowedDialog( context, message: "You are not entitled for this service. If you wish to continue with this service, you may require another approval.", ); if (confirm) { setState(() { exceptionalClass[index] = "1"; selectedClasses[index] = newValue['dropdown_key']; }); print( "Selected Purpose: ${selectedClasses[index]}", ); } else { setState(() { exceptionalClass[index] = "0"; selectedClasses[index] = null; // ❌ Clear selection if user cancels }); print( "selectedItem resolved to: ${selectedClasses[index]}", ); } return; } else if (newValue != null && newValue['is_allowed'] == 'yes') { setState(() { exceptionalClass[index] = "0"; selectedClasses[index] = newValue?['dropdown_key']; print( "Selected Purpose: ${selectedClasses[index]}", ); }); } } : null, ), ), ), ), ), if (errorMessages["class_$index"] != null) Padding( padding: const EdgeInsets.only(top: 4), child: Text( errorMessages["class_$index"]!, style: GoogleFonts.poppins(fontSize: 12, color: Colors.red), ), ), ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Date*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_date${index}Focused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: () { focusNodes["_date${index}FocusNode"]?.requestFocus(); _selectCheckOutDate(context); }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["_date${index}FocusNode"], // controller: _dateController, controller: textControllers["_date${index}Controller"], readOnly: true, style: const TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Select Date", labelStyle: const TextStyle( fontSize: 12, color: Colors.grey, ), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), // border: OutlineInputBorder( // borderRadius: BorderRadius.circular(8), // borderSide: BorderSide( // color: // (focusStates["_date${index}Focused"] ?? false) // ? layoutColor! // : Colors.white, // width: 0.5, // ), // ), // enabledBorder: OutlineInputBorder( // borderSide: BorderSide( // color: // (focusStates["_date${index}Focused"] ?? false) // ? layoutColor // : Colors.white, // // : const Color(0xFFD6D5E6), // width: 0.5, // // const Color(0xFFD6D5E6), // ), // ), // focusedBorder: OutlineInputBorder( // borderSide: BorderSide(color: layoutColor, width: 1), // ), // contentPadding: const EdgeInsets.symmetric( // horizontal: 10, // vertical: 5, // ), suffixIcon: const Icon( Icons.calendar_today, size: 16, color: Colors.grey, ), ), ), ), ), ), ), if (errorMessages["date_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["date_$index"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Time*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_time${index}Focused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: () { focusNodes["_time${index}FocusNode"]?.requestFocus(); textControllers["_time${index}Controller"]?.text = ""; _clearError("time_$index"); // validateTimeDifference(index); // _selectCheckOutTime(context); _selectCheckOutTime(context, index, () { validateTimeDifference(index); setState( () {}, ); // βœ… Force rebuild to show the error immediately }); }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["_time${index}FocusNode"], // controller: _timeController, controller: textControllers["_time${index}Controller"], readOnly: true, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Time", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), suffixIcon: Icon( Icons.access_time, size: 16, color: Colors.grey, ), ), ), ), // Focus( // focusNode: focusNodes["_time${index}FocusNode"], // onFocusChange: (hasFocus) { // setState(() { // focusStates["_time${index}Focused"] = hasFocus; // }); // }, // child: GestureDetector( // // // onTap: () { // // Request focus when user taps // focusNodes["_time${index}FocusNode"]?.requestFocus(); // }, ), ), ), if (errorMessages["time_$index"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["time_$index"]!, style: TextStyle(color: Colors.red, fontSize: 12), ), ], ], ), if (selectedTripType == "Multitrip") if(multiTripRowCount > 1) Padding( padding: const EdgeInsets.only(top: 10), child: SizedBox( width: double.infinity, // ensure full width child: ElevatedButton( style: ElevatedButton.styleFrom( minimumSize: const Size.fromHeight(48), // set height backgroundColor: Colors.red, foregroundColor: Colors.white, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), ), onPressed: () { print("Pressed: $index"); removeTrip(index); }, child: const Text( "Remove", style: TextStyle(fontSize: 14), ), ), ), ), // Container( // // color: Colors.blueGrey, // // padding: const EdgeInsets.only(top: 50, bottom: 50), // child: IconButton( // onPressed: () { // removeTrip(index); // }, // icon: Icon(Icons.close, color: Colors.redAccent, size: 20), // ), // ), ], ), ), ), ]; } List _buildThirdRow(bool isDesktop) { List visa_available = widget.apiData?['flight_visa_available'] ?? []; // Default selected value List> dropdownItems = visa_available .map( (item) => DropdownMenuItem( value: item['dropdown_key'], child: Text(item['dropdown_value']), ), ) .toList(); selectedvisa_available ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text( "No options available", style: TextStyle(color: Colors.grey), ), ), ); } // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text( // "Visa Required", // style: GoogleFonts.poppins( // fontSize: 12, // fontWeight: FontWeight.w600, // color: Color(0xFF575A74), // ), // ), // SizedBox(height: 5), // CustomTextFieldItnerarySubWrapper( // // isFocused: _tripTypeFocused, // isFocused: focusStates["_visa1Focused"] ?? false, // isDesktop: isDesktop, // // width: isDesktop // // ? MediaQuery.of(context).size.width * 0.34 // // : MediaQuery.of(context).size.width * 0.66, // child: SizedBox( // height: 40, // child: DropdownButtonFormField( // // focusNode: _tripTypeFocusNode, // Assign the correct focus node // focusNode: focusNodes["_visa1FocusNode"], // value: selectedvisa_available, // style: TextStyle(fontSize: 12), // decoration: InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric( // horizontal: 10, // ), // Proper padding // ), // onChanged: // visa_available.isNotEmpty // ? (newValue) { // setState(() { // selectedvisa_available = newValue; // // selectedTripType = "Oneway"; // // Reset `multiTripRowCount` when switching away from Multitrip // }); // print( // "Updating form data: Flight -> trip_type -> $selectedvisa_available", // ); // // // _initializeRows(); // } // : null, // // items: dropdownItems, // ), // ), // ), // ], // ), //********// return isDesktop ? [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Visa Required", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // use same wrapper as class // isFocused: focusStates["_visa1Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 35, width: double.infinity, child: Focus( focusNode: focusNodes["_visa1FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_visa1Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_visa1FocusNode"]?.requestFocus(); }, child: DropdownSearch>( items: visa_available.cast>(), selectedItem: visa_available.firstWhere( (item) => item['dropdown_key'] == selectedvisa_available, orElse: () => {}, ), itemAsString: (item) => item['dropdown_value'] ?? '', 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: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_visa1Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_visa1Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 1), ), contentPadding: const EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( "Select Option", style: GoogleFonts.poppins( color: Colors.grey, fontSize: 13, ), ); } return Text( selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ); }, onChanged: visa_available.isNotEmpty ? (Map? newValue) { setState(() { selectedvisa_available = newValue?['dropdown_key']; print("Selected Visa: $selectedvisa_available"); }); } : null, ), ), ), ), ), ], ), if (isDesktop) SizedBox(width: 20), SizedBox(height: 5), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Comments", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_comments1Focused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, child: TextField( focusNode: focusNodes["_comments1FocusNode"], // controller: _commentsController, controller: textControllers["_comments1Controller"], // maxLines: 6, // keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 1), ), ), ), ], ), if (isDesktop) Spacer(), SizedBox(height: 5), Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: _handleAction(isDesktop), ), ], ), ] : [ Card( elevation: 1, margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), color: const Color(0xFFF5F5F5), // Light grey shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: Padding( padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Visa Required", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // use same wrapper as class // isFocused: focusStates["_visa1Focused"] ?? false, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, child: SizedBox( height: 35, width: double.infinity, child: Focus( focusNode: focusNodes["_visa1FocusNode"], onFocusChange: (hasFocus) { setState(() { focusStates["_visa1Focused"] = hasFocus; }); }, child: GestureDetector( // onTap: () { // Request focus when user taps focusNodes["_visa1FocusNode"]?.requestFocus(); }, child: DropdownSearch>( items: visa_available.cast>(), selectedItem: visa_available.firstWhere( (item) => item['dropdown_key'] == selectedvisa_available, orElse: () => {}, ), itemAsString: (item) => item['dropdown_value'] ?? '', 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: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["_visa1Focused"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["_visa1Focused"] ?? false) ? layoutColor : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor, width: 1), ), contentPadding: const EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( "Select Option", style: GoogleFonts.poppins( color: Colors.grey, fontSize: 13, ), ); } return Text( selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ); }, onChanged: visa_available.isNotEmpty ? (Map? newValue) { setState(() { selectedvisa_available = newValue?['dropdown_key']; print("Selected Visa: $selectedvisa_available"); }); } : null, ), ), ), ), ), ], ), if (isDesktop) SizedBox(width: 20), SizedBox(height: 5), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Comments", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( isFocused: focusStates["_comments1Focused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null, child: TextField( focusNode: focusNodes["_comments1FocusNode"], // controller: _commentsController, controller: textControllers["_comments1Controller"], // maxLines: 6, // keyboardType: TextInputType.multiline, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 1), ), ), ), ], ), ], ), ), ), if (isDesktop) Spacer(), SizedBox(height: 10), Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: _handleAction(isDesktop), ), ], ), ]; } List _buildvisa(bool isDesktop) { List visa_available = widget.apiData?['flight_visa_available'] ?? []; // Default selected value List> dropdownItems = visa_available .map( (item) => DropdownMenuItem( value: item['dropdown_key'], child: Text(item['dropdown_value']), ), ) .toList(); selectedvisa_available ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text( "No options available", style: TextStyle(color: Colors.grey), ), ), ); } return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Visa Required", style: TextStyle( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( // isFocused: _tripTypeFocused, isFocused: focusStates["_visa1Focused"] ?? false, isDesktop: isDesktop, // width: // isDesktop // ? MediaQuery.of(context).size.width * 0.34 // : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownButtonFormField( // focusNode: _tripTypeFocusNode, // Assign the correct focus node focusNode: focusNodes["_visa1FocusNode"], value: selectedvisa_available, style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 10, ), // Proper padding ), onChanged: visa_available.isNotEmpty ? (newValue) { setState(() { selectedvisa_available = newValue; // selectedTripType = "Oneway"; // Reset `multiTripRowCount` when switching away from Multitrip }); print( "Updating form data: Flight -> trip_type -> $selectedvisa_available", ); // _initializeRows(); } : null, items: dropdownItems, ), ), ), ], ), ]; } List _handleAction(bool isDesktop) { return [ // Close Button ElevatedButton( onPressed: () { widget.onClose("Flight", false); // widget.onClose(false); // Close the dialog or screen }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Close", style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons // Save Changes Button ElevatedButton( onPressed: () { handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Save Changes", style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; } }