ts-tat/lib/Screens/itnerary/flights.dart
2025-03-28 17:43:35 +05:30

1242 lines
38 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.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 Map<String, dynamic>? apiData;
final String? loginUser;
final Function(bool) onClose;
final Function(Map<String,dynamic>) onSaveFlight;
final Map<String,dynamic>? selectedItem;
FlightScreen({ required this.apiData,required this.loginUser,
required this.onClose, required this.onSaveFlight,required this.selectedItem});
@override
_FlightScreenState createState() => _FlightScreenState();
}
class _FlightScreenState extends State<FlightScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Map<String, String?> selectedValues = {};
String? selectedTripType;
Map<int, String?> selectedClasses = {}; // Store class selection for each trip
String? selectedvisa_available;
int multiTripRowCount = 1;
List<String> dataHeader = ["_tripType", "_class", "_from", "_to", "_date","_visa", "_time", "_comments"];
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
Map<String, TextEditingController> textControllers = {};
Map<String, String> fieldErrors = {}; // Holds error messages
List<List<Widget>> rowBuilders = []; // Holds row widgets
List<TextEditingController> controllers = []; // Dynamic controllers
Map<String, String> errorMessages = {};
@override
void initState() {
super.initState();
// _initializeRows();
// List<dynamic> 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<dynamic> 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"));
}
}
int getRowCount() {
if (selectedTripType == "RoundTrip") {
return 2;
} else if (selectedTripType == "Multitrip") {
return multiTripRowCount;
}
return 1; // Default for Oneway
}
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<String, dynamic> get flightsData{
List<Map<String, dynamic>> 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++) {
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<String,dynamic> 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<dynamic> 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<String, dynamic>;
int index = i + 1; // Use 1-based indexing to match the form
selectedClasses[index] = trip["class"].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"]);
}
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);
print("FlightData - $flightsData");
}
bool validateFields() {
errorMessages.clear(); // Reset errors
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 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 (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["_time${i}Controller"]?.text.trim().isEmpty ?? true) {
errorMessages["time_$i"] = "Required";
}
}
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<String, TextEditingController> 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<int, String?> 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
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),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Align(
alignment: Alignment.centerRight,
child: InkWell(
onTap: () {
widget.onClose(false);
},
child: Icon(
Icons.close,
size: 18,
color: Color(0xFF575A74),
),
),
),
Text("Flight Booking",
style:
TextStyle(fontSize: 18, fontWeight: FontWeight.bold,color: Color(0xFF575A74))),
SizedBox(
height: 6,
),
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
)
],
),
),
),
);
});
}
// void _addNewRow() {
// setState(() {
// rowBuilders.add(_builClassType(false));
// controllers.add(TextEditingController());
// });
// }
List<Widget> _buildAccomadtionForm (bool isDesktop) {
List<Widget> buildResponsiveRow(List<Widget> children) {
return [
isDesktop ? Row(children: children) : Column(children: children),
SizedBox(height: 10),
];
}
List<List<Widget>> rowBuilders = [
_builClassType(isDesktop,1),
_buildSecondRow(isDesktop,1)
];
List<List<Widget>> 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<Widget> firstRow = _builClassType(isDesktop, index + 1);
List<Widget> 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(
backgroundColor: Colors.blueAccent,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: BorderSide(color: Colors.blueAccent, width: 2),
),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2),
) ,
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(fontSize: 10,fontWeight:FontWeight.bold),),
),
),
...buildResponsiveRow(_buildvisa(isDesktop)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Trip Type",
style: TextStyle(
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<Widget> _buildTripType(bool isDesktop){
List<dynamic> purposeList = widget.apiData?['flight_trip_type'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_value'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
return [
CustomTextFieldWrapper(
// isFocused: _tripTypeFocused,
isFocused: focusStates["_tripType1Focused"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
// focusNode: _tripTypeFocusNode, // Assign the correct focus node
focusNode: focusNodes["_tripType1FocusNode"],
value: selectedTripType,
style: TextStyle(fontSize: 12),
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,
),
),
),
];
}
List<Widget> _buildDelete(bool isDesktop, int index){
return [
Align(
alignment: Alignment.center,
child: Text(
"Trip ${index}",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
),
SizedBox(
width: isDesktop? 1000 : 80, // Ensure full width
child: Divider(
thickness: 1, // Make it more visible
),
),
IconButton(onPressed: (){
removeTrip(index);
},
icon: Icon(Icons.delete),color: Colors.red,iconSize: 20,)
];
}
List<Widget> _builClassType(bool isDesktop, int index){
List<dynamic> purposeList = widget.apiData?['flight_class'] ?? [];
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_key'],
child: Text(item['dropdown_value']),
)).toList();
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
value: null,
child: Text("No options available", style: TextStyle(color: Colors.grey)),
),
);
}
// Default selected value
selectedClasses[index] ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (selectedTripType == "Multitrip" )
isDesktop?
SizedBox(
width: MediaQuery.of(context).size.width * 0.89,
child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [..._buildDelete(isDesktop, index)]),
)
: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [..._buildDelete(isDesktop, index)]),
SizedBox(height: 20),
Text(
"Class $index *",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["_class${index}Focused"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
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,
),
),
),
],
),
];
}
List<Widget> _buildSecondRow(bool isDesktop, int index) {
DateTime? _selectedCheckOutDate;
TimeOfDay? _selectedCheckOutTime;
Future<void> _selectCheckOutDate(BuildContext context) async {
DateTime now = DateTime.now();
DateTime today = DateTime(now.year, now.month, now.day);
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: _selectedCheckOutDate != null && _selectedCheckOutDate!.isAfter(today)
? _selectedCheckOutDate!
: today,
firstDate: today,
lastDate: DateTime(2100),
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
// _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
textControllers["_date${index}Controller"]?.text = DateFormat('yyyy-MM-dd').format(pickedDate);
});
}
}
Future<void> _selectCheckOutTime(BuildContext context) async {
TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
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;
});
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"From",
style: TextStyle(
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: 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),
),
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"To",
style: TextStyle(
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: TextField(
focusNode: focusNodes["_to${index}FocusNode"],
controller: textControllers["_to${index}Controller"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "To",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
if (errorMessages["to_place_$index"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_date${index}Focused"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutDate(context),
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["_date${index}FocusNode"],
// controller: _dateController,
controller: textControllers["_date${index}Controller"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select Date",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: Icon(Icons.calendar_today,
size: 16, color: Colors.grey),
),
),
),
),
),
),
if (errorMessages["date_$index"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_timeFocused"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["_time${index}FocusNode"],
// controller: _timeController,
controller: textControllers["_time${index}Controller"],
style: const TextStyle(fontSize: 12),
decoration: const InputDecoration(
labelText: "Select 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),
),
),
),
),
),
),
if (errorMessages["time_$index"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
"Required",
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["_comments1Focused"] ?? false,
isDesktop: isDesktop,
width: isDesktop
? MediaQuery.of(context).size.width * 0.4
: MediaQuery.of(context).size.width * 0.66,
child: TextField(
focusNode: focusNodes["_comments1FocusNode"],
// controller: _commentsController,
controller: textControllers["_comments1Controller"],
maxLines: 6,
keyboardType: TextInputType.multiline,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Description",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 4),
),
),
),
],
)
];
}
List<Widget> _buildvisa(bool isDesktop){
List<dynamic> visa_available = widget.apiData?['flight_visa_available'] ?? [];
// Default selected value
List<DropdownMenuItem<String>> dropdownItems = visa_available
.map((item)=>DropdownMenuItem<String>(
value: item['dropdown_key'],
child: Text(item['dropdown_value']),
)).toList();
selectedvisa_available ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
if (dropdownItems.isEmpty) {
dropdownItems.add(
DropdownMenuItem<String>(
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),
CustomTextFieldWrapper(
// isFocused: _tripTypeFocused,
isFocused: focusStates["_visa1Focused"] ?? false,
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: DropdownButtonFormField<String>(
// 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<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
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: TextStyle(color: Colors.white, fontSize: 14),
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
handleSave();
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Primary color for save
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
),
child: Text(
"Save Changes",
style: TextStyle(color: Colors.white, fontSize: 14),
),
),
];
}
}