From 0368c05c354293d840b27e53ff1df6721c3c9288 Mon Sep 17 00:00:00 2001 From: venba-Inspriron-3558 Date: Fri, 6 Jun 2025 13:02:40 +0530 Subject: [PATCH] flight.dart form page --- lib/Screens/itnerary/flights.dart | 1038 +++++++++++++++++------------ 1 file changed, 627 insertions(+), 411 deletions(-) diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index b4ca591..278a7e0 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -21,19 +21,19 @@ class FlightScreen extends StatefulWidget { 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); + 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(); @@ -70,7 +70,7 @@ class FlightScreenState extends State { "_date", "_visa", "_time", - "_comments" + "_comments", ]; Map focusNodes = {}; @@ -148,14 +148,18 @@ class FlightScreenState extends State { // 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")); + 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(); @@ -178,17 +182,16 @@ class FlightScreenState extends State { } Map getFlightTripDateRange( - List> flightData) { - final allTrips = flightData - .expand((flight) => flight['trips'] ?? []) - .whereType>() - .toList(); + List> flightData, + ) { + final allTrips = + flightData + .expand((flight) => flight['trips'] ?? []) + .whereType>() + .toList(); if (allTrips.isEmpty) { - return { - 'firstTripDate': null, - 'lastTripDate': null, - }; + return {'firstTripDate': null, 'lastTripDate': null}; } allTrips.sort((a, b) { @@ -293,9 +296,10 @@ class FlightScreenState extends State { print("Text Controllers KeysII: ${textControllers.keys.toList()}"); // Determine the row count based on selectedTripType - int rowCount = selectedTripType == "Roundtrip" - ? 2 - : selectedTripType == "Multitrip" + int rowCount = + selectedTripType == "Roundtrip" + ? 2 + : selectedTripType == "Multitrip" ? multiTripRowCount : 1; @@ -476,10 +480,12 @@ class FlightScreenState extends State { // 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"]); + 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 && @@ -541,10 +547,9 @@ class FlightScreenState extends State { final currDateTime = format.parse("$currDateStr $currTimeStr"); if (!currDateTime.isAfter(prevDateTime)) { - errorMessages["time_$index"] = "Must be after previous time"; + errorMessages["time_$index"] = "30 mins gap required"; } else if (currDateTime.difference(prevDateTime).inMinutes < 30) { - errorMessages["time_$index"] = - "Must be least 30 mins after previous time"; + errorMessages["time_$index"] = "30 mins gap required"; } else { errorMessages.remove("time_$index"); } @@ -665,32 +670,34 @@ class FlightScreenState extends State { @override Widget build(BuildContext context) { - return ResponsiveBuilder(builder: (context, sizingInfo) { - bool isMobile = sizingInfo.isMobile; - bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; + 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.all(20.0), - child: Center( - child: Column(children: _buildAccomadtionForm(isDesktop)), + 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.all(20.0), + child: Center( + child: Column(children: _buildAccomadtionForm(isDesktop)), + ), ), - ) - ], + ], + ), ), ), - ), - ); - }); + ); + }, + ); } List _buildAccomadtionForm(bool isDesktop) { @@ -703,14 +710,14 @@ class FlightScreenState extends State { List> rowBuilders = [ // _builClassType(isDesktop, 1), - _buildSecondRow(isDesktop, 1) + _buildSecondRow(isDesktop, 1), ]; List> rowRoundBuilders = [ // _builClassType(isDesktop, 1), _buildSecondRow(isDesktop, 1), // _builClassType(isDesktop, 2), - _buildSecondRow(isDesktop, 2) + _buildSecondRow(isDesktop, 2), ]; print("Trip Type Selected: $selectedTripType"); @@ -740,7 +747,6 @@ class FlightScreenState extends State { // ...List.generate(multiTripRowCount, (index) => // buildResponsiveRow(_builClassType(isDesktop, index + 1) + _buildSecondRow(isDesktop, index + 1)) // ).expand((row) => row), - if (selectedTripType == "Multitrip") Align( alignment: Alignment.centerRight, @@ -809,41 +815,42 @@ class FlightScreenState extends State { Text( "Trip Type", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), isDesktop ? Row(children: _buildTripType(isDesktop)) - : Column(children: _buildTripType(isDesktop)) + : Column(children: _buildTripType(isDesktop)), ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + 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(); + 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)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -858,9 +865,10 @@ class FlightScreenState extends State { height: 40, width: double.infinity, child: DropdownSearch( - items: purposeList - .map((item) => item['dropdown_value'] as String) - .toList(), + items: + purposeList + .map((item) => item['dropdown_value'] as String) + .toList(), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, @@ -876,29 +884,35 @@ class FlightScreenState extends State { errorMessages.clear(); }); print( - "Updating form data: Flight -> trip_type -> $selectedTripType"); + "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), - ), - ), + 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 - ), - ), + 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 + ), + ), ), ), @@ -999,9 +1013,9 @@ class FlightScreenState extends State { return [ Container( padding: const EdgeInsets.all(10), + // padding: const EdgeInsets.only(left: 10, right: 10), // color: Colors.white, - child: Text( "Trip ${index}", style: TextStyle( @@ -1012,17 +1026,14 @@ class FlightScreenState extends State { ), ), SizedBox( - width: isDesktop - ? MediaQuery.of(context).size.width * 0.58 - : 80, // Ensure full width + 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, - ), + Divider(color: Color(0xFF8B8FB2), thickness: 0.5, height: 20), Container( // padding: EdgeInsets.all(4), color: Colors.white, // Background to avoid overlapping @@ -1064,7 +1075,6 @@ class FlightScreenState extends State { // ], // ), // ), - Container( // color: Colors.white, // padding: const EdgeInsets.only(left: 10, right: 10), @@ -1076,7 +1086,7 @@ class FlightScreenState extends State { color: Colors.blueAccent, iconSize: 20, ), - ) + ), ]; } @@ -1085,19 +1095,24 @@ class FlightScreenState extends State { List purposeList = widget.apiDataForClass?['flight_class'] ?? []; - List> dropdownItems = purposeList - .map((item) => DropdownMenuItem( - value: item['dropdown_key'], - child: Text(item['dropdown_value']), - )) - .toList(); + 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)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -1120,8 +1135,9 @@ class FlightScreenState extends State { flightLastTripDateNotifier.value != null && flightLastTripDateNotifier.value!.isNotEmpty) { try { - final tripDate = DateFormat('dd-MM-yyyy') - .parseStrict(flightLastTripDateNotifier.value!); + final tripDate = DateFormat( + 'dd-MM-yyyy', + ).parseStrict(flightLastTripDateNotifier.value!); if (tripDate.isAfter(today)) { firstDate = tripDate; } @@ -1133,8 +1149,9 @@ class FlightScreenState extends State { textControllers["_date${index - 1}Controller"]?.text; if (previousDateString != null && previousDateString.isNotEmpty) { try { - final previousDate = - DateFormat('dd-MM-yyyy').parseStrict(previousDateString); + final previousDate = DateFormat( + 'dd-MM-yyyy', + ).parseStrict(previousDateString); if (previousDate.isAfter(today)) { firstDate = previousDate; } @@ -1144,10 +1161,11 @@ class FlightScreenState extends State { } } - DateTime initialDate = _selectedCheckOutDate != null && - _selectedCheckOutDate!.isAfter(firstDate) - ? _selectedCheckOutDate! - : firstDate; + DateTime initialDate = + _selectedCheckOutDate != null && + _selectedCheckOutDate!.isAfter(firstDate) + ? _selectedCheckOutDate! + : firstDate; DateTime? pickedDate = await showDatePicker( context: context, @@ -1160,14 +1178,18 @@ class FlightScreenState extends State { setState(() { _selectedCheckOutDate = pickedDate; // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate); - textControllers["_date${index}Controller"]?.text = - DateFormat('dd-MM-yyyy').format(pickedDate); + textControllers["_date${index}Controller"]?.text = DateFormat( + 'dd-MM-yyyy', + ).format(pickedDate); }); } } Future _selectCheckOutTime( - BuildContext context, int index, VoidCallback onPicked) async { + BuildContext context, + int index, + VoidCallback onPicked, + ) async { TimeOfDay? pickedTime = await showTimePicker( context: context, initialTime: _selectedCheckOutTime ?? TimeOfDay.now(), @@ -1179,8 +1201,13 @@ class FlightScreenState extends State { // 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), + DateTime( + now.year, + now.month, + now.day, + pickedTime.hour, + pickedTime.minute, + ), ); // _timeController.text = formattedTime; textControllers["_time${index}Controller"]?.text = formattedTime; @@ -1208,50 +1235,57 @@ class FlightScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "From", + "From*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - // isFocused: _fromFocus, - isFocused: focusStates["_from${index}Focused"] ?? false, - // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: isCountryLoading - ? Center(child: CircularProgressIndicator()) - : DropdownSearch( - selectedItem: selectedFrom[index] != null - ? countryMap[selectedFrom[index]] - : null, + // isFocused: _fromFocus, + isFocused: focusStates["_from${index}Focused"] ?? false, + // isFocused: focusStates["_from${fieldIndex}Focused"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : DropdownSearch( + selectedItem: + selectedFrom[index] != null + ? countryMap[selectedFrom[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), + decoration: InputDecoration( + hintText: "Search...", + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 1, ), - style: TextStyle(fontSize: 12)), - 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), // 👈 Set your desired text size here ), + style: TextStyle(fontSize: 12), ), + 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, + ), // 👈 Set your desired text size here + ), + ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( @@ -1260,109 +1294,110 @@ class FlightScreenState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 1), ), ), - dropdownBuilder: (context, selectedItem) => Align( - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select", - style: TextStyle(fontSize: 12), - ), - ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select", + style: TextStyle(fontSize: 12), + ), + ), onChanged: (String? newValue) { setState(() { // selectedFrom[index] = countryMap.entries // .firstWhere((entry) => entry.value == newValue) // .key; - selectedFrom[index] = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedFrom[index] = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; print(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), - // ), - // ), - // ), - ), + // 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( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - SizedBox( - width: 20, - ) - else - SizedBox( - height: 8, - ), + if (isDesktop) SizedBox(width: 20) else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "To", + "To*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( - isFocused: focusStates["_to${index}Focused"] ?? false, - isDesktop: isDesktop, - child: SizedBox( - height: 40, - child: isCountryLoading - ? Center(child: CircularProgressIndicator()) - : DropdownSearch( - selectedItem: selectedTo[index] != null - ? countryMap[selectedTo[index]] - : null, + isFocused: focusStates["_to${index}Focused"] ?? false, + isDesktop: isDesktop, + child: SizedBox( + height: 40, + child: + isCountryLoading + ? Center(child: CircularProgressIndicator()) + : DropdownSearch( + 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), + decoration: InputDecoration( + hintText: "Search...", + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 1, ), - style: TextStyle(fontSize: 12)), - 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), // 👈 Set your desired text size here ), + style: TextStyle(fontSize: 12), ), + 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, + ), // 👈 Set your desired text size here + ), + ), ), items: countryMap.values.toList(), dropdownDecoratorProps: DropDownDecoratorProps( @@ -1371,102 +1406,193 @@ class FlightScreenState extends State { contentPadding: EdgeInsets.symmetric(horizontal: 1), ), ), - dropdownBuilder: (context, selectedItem) => Align( - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select Country", - style: TextStyle(fontSize: 12), - ), - ), + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select Country", + style: TextStyle(fontSize: 12), + ), + ), onChanged: (String? newValue) { setState(() { - selectedTo[index] = countryMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; + selectedTo[index] = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; print(selectedTo[index]); }); }, ), - )), + ), + ), if (errorMessages["to_place_$index"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + 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)), + 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 + height: 35, + width: double.infinity, + child: DropdownSearch>( + items: purposeList.cast>(), + selectedItem: purposeList.firstWhere( + (item) => item['dropdown_key'] == selectedClasses[index], + 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, ), - onChanged: purposeList.isNotEmpty - ? (newValue) { - setState(() { - selectedClasses[index] = newValue; - }); - - print(selectedClasses[index]); - } - : null, - items: dropdownItems, + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: const DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, ), + ), + ), + 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) { + setState(() { + selectedClasses[index] = newValue?['dropdown_key']; + print("Selected Class: ${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, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Date", + "Date*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -1489,8 +1615,11 @@ class FlightScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: Icon(Icons.calendar_today, - size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.calendar_today, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -1499,28 +1628,21 @@ class FlightScreenState extends State { ), if (errorMessages["date_$index"] != null) ...[ SizedBox(height: 5), // Space before error message - Text( - "Required", - style: TextStyle(color: Colors.red, fontSize: 12), - ), + Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), - if (isDesktop) - Spacer() - else - SizedBox( - height: 8, - ), + if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Time", + "Time*", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldItnerarySubWrapper( @@ -1538,7 +1660,8 @@ class FlightScreenState extends State { _selectCheckOutTime(context, index, () { validateTimeDifference(index); setState( - () {}); // ✅ Force rebuild to show the error immediately + () {}, + ); // ✅ Force rebuild to show the error immediately }); }, child: AbsorbPointer( @@ -1554,8 +1677,11 @@ class FlightScreenState extends State { floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), - suffixIcon: - Icon(Icons.access_time, size: 16, color: Colors.grey), + suffixIcon: Icon( + Icons.access_time, + size: 16, + color: Colors.grey, + ), ), ), ), @@ -1579,13 +1705,9 @@ class FlightScreenState extends State { onPressed: () { removeTrip(index); }, - icon: Icon( - Icons.close, - color: Colors.redAccent, - size: 20, - ), + icon: Icon(Icons.close, color: Colors.redAccent, size: 20), ), - ) + ), ]; } @@ -1594,12 +1716,15 @@ class FlightScreenState extends State { 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(); + 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; @@ -1608,78 +1733,166 @@ class FlightScreenState extends State { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } return [ + // 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, + // ), + // ), + // ), + // ], + // ), + //********// Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Visa Required", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), - CustomTextFieldItnerarySubWrapper( - // isFocused: _tripTypeFocused, + CustomTextFieldWrapper( // use same wrapper as class 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 + height: 35, + width: double.infinity, + 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: const DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: 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 - ? (newValue) { - setState(() { - selectedvisa_available = newValue; - // selectedTripType = "Oneway"; - // Reset `multiTripRowCount` when switching away from Multitrip - }); - print( - "Updating form data: Flight -> trip_type -> $selectedvisa_available"); - - // _initializeRows(); - } + ? (Map? newValue) { + setState(() { + selectedvisa_available = newValue?['dropdown_key']; + print("Selected Visa: $selectedvisa_available"); + }); + } : null, - - items: dropdownItems, ), ), ), ], ), - if (isDesktop) - SizedBox( - width: 20, - ), - SizedBox( - height: 5, - ), + 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)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( @@ -1705,9 +1918,7 @@ class FlightScreenState extends State { ], ), if (isDesktop) Spacer(), - SizedBox( - height: 5, - ), + SizedBox(height: 5), Column( children: [ Row( @@ -1724,12 +1935,15 @@ class FlightScreenState extends State { 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(); + 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; @@ -1738,8 +1952,10 @@ class FlightScreenState extends State { dropdownItems.add( DropdownMenuItem( value: null, - child: Text("No options available", - style: TextStyle(color: Colors.grey)), + child: Text( + "No options available", + style: TextStyle(color: Colors.grey), + ), ), ); } @@ -1751,18 +1967,20 @@ class FlightScreenState extends State { Text( "Visa Required", style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( // isFocused: _tripTypeFocused, isFocused: focusStates["_visa1Focused"] ?? false, isDesktop: isDesktop, - width: isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.66, + width: + isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownButtonFormField( @@ -1772,22 +1990,25 @@ class FlightScreenState extends State { style: TextStyle(fontSize: 12), decoration: InputDecoration( border: InputBorder.none, - contentPadding: - EdgeInsets.symmetric(horizontal: 10), // Proper padding + 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"); + 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, + // _initializeRows(); + } + : null, items: dropdownItems, ), @@ -1807,9 +2028,7 @@ class FlightScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( @@ -1818,7 +2037,6 @@ class FlightScreenState extends State { ), ), SizedBox(width: 10), // Space between buttons - // Save Changes Button ElevatedButton( onPressed: () { @@ -1826,9 +2044,7 @@ class FlightScreenState extends State { }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text(