ts-tat/lib/Screens/itnerary/train.dart
2025-07-29 15:24:58 +05:30

1410 lines
50 KiB
Dart

import 'package:dropdown_search/dropdown_search.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:responsive_builder/responsive_builder.dart';
import '../../services/apiService.dart';
import '../../utils/auth_utils.dart';
import '../../widgets/custom_confirmation_dialog.dart';
import '../../widgets/custom_text_field.dart';
import '../../widgets/custom_text_itnerary_sub.dart';
class TrainScreen extends StatefulWidget {
final Map<String, dynamic>? apiData;
final Map<String, dynamic>? apiDataForClass;
final Function(Map<String, dynamic>) onSavetrain;
final Function(String, bool) onClose;
final Map<String, dynamic>? selectedItem;
final String? loginUser;
final String? tripType;
TrainScreen({
required this.onClose,
this.apiData,
required this.onSavetrain,
required this.selectedItem,
required this.loginUser,
this.apiDataForClass,
this.tripType,
});
@override
_TrainScreenState createState() => _TrainScreenState();
}
class _TrainScreenState extends State<TrainScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
ApiService apiService = ApiService();
bool isCountryLoading = true;
Map<String, String> countryMap = {}; // <--- instead of late
late List<String> countryCodes;
Map<String, String?> selectedValues = {};
final FocusNode _trainNoFocusNode = FocusNode();
final FocusNode _typeOfClassFocusNode = FocusNode();
final FocusNode _fromFocusNode = FocusNode();
final FocusNode _toFocusNode = FocusNode();
final FocusNode _dateFocusNode = FocusNode();
final FocusNode _timeFocusNode = FocusNode();
final FocusNode _commentsFocusNode = FocusNode();
late TextEditingController _trainNoController = TextEditingController();
late TextEditingController _hotelNameController = TextEditingController();
late TextEditingController _fromController = TextEditingController();
late TextEditingController _toController = TextEditingController();
late TextEditingController _dateController = TextEditingController();
late TextEditingController _timeController = TextEditingController();
late TextEditingController _trainCommentsController = TextEditingController();
bool _trainNoFocused = false;
bool _typeOfClassFocus = false;
bool _fromFocus = false;
bool _toFocus = false;
bool _dateFocus = false;
bool _timeFocus = false;
bool _commentsFocus = false;
String? selectedClass;
String? exceptionalClass = "0";
String? selectedFrom;
String? selectedTo;
Color layoutColor = Colors.grey;
Map<String, String> errorMessages = {};
Map<String, dynamic> get trainData {
Map<String, dynamic> data = {
"train_no": _trainNoController.text,
"class": selectedClass,
"from_station": selectedFrom,
// "from_station": _fromController.text,
"to_station": selectedTo,
// "to_station": _toController.text,
"is_this_exceptional": exceptionalClass,
"date": _dateController.text,
"time": _timeController.text,
"comments": _trainCommentsController.text,
"created_by": widget.loginUser,
"updated_by": widget.loginUser,
};
if (widget.selectedItem != null) {
if (widget.selectedItem?["indx"] != null &&
widget.selectedItem?["indx"] != 0) {
data["indx"] = widget.selectedItem!["indx"];
} else if (widget.selectedItem?["train_id"] != null &&
widget.selectedItem?["train_id"] != 0) {
data["train_id"] = widget.selectedItem!["train_id"];
}
}
return data;
}
TextEditingController initController(String key) {
return TextEditingController(text: widget.selectedItem?[key] ?? "");
}
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
_trainNoFocusNode.addListener(() {
setState(() {
_trainNoFocused = _trainNoFocusNode.hasFocus;
});
});
_typeOfClassFocusNode.addListener(() {
setState(() {
_typeOfClassFocus = _typeOfClassFocusNode.hasFocus;
});
});
_fromFocusNode.addListener(() {
setState(() {
_fromFocus = _fromFocusNode.hasFocus;
});
});
_toFocusNode.addListener(() {
setState(() {
_toFocus = _toFocusNode.hasFocus;
});
});
_dateFocusNode.addListener(() {
setState(() {
_dateFocus = _dateFocusNode.hasFocus;
});
});
_timeFocusNode.addListener(() {
setState(() {
_timeFocus = _timeFocusNode.hasFocus;
});
});
_commentsFocusNode.addListener(() {
setState(() {
_commentsFocus = _commentsFocusNode.hasFocus;
});
});
_trainCommentsController = initController("comments");
_trainNoController = initController("train_no");
// _fromController = initController("from_station");
_toController = initController("to_station");
_dateController = initController("date");
_timeController = initController("time");
if (widget.selectedItem != null &&
widget.selectedItem!["is_this_exceptional"] != null) {
exceptionalClass = widget.selectedItem!["is_this_exceptional"].toString();
}
// Set the selected value if available
if (widget.selectedItem != null && widget.selectedItem!["class"] != null) {
selectedClass = widget.selectedItem!["class"].toString();
}
// if (widget.selectedItem != null &&
// widget.selectedItem!["from_station"] != null) {
// final fromCode = widget.selectedItem!["from_station"].toString();
// if (countryMap.containsKey(fromCode)) {
// selectedFrom = fromCode;
// }
// }
if (widget.selectedItem != null &&
widget.selectedItem!["from_station"] != null) {
selectedFrom = widget.selectedItem!["from_station"].toString();
print("Selected From CODE = $selectedFrom");
}
if (widget.selectedItem != null &&
widget.selectedItem!["to_station"] != null) {
selectedTo = widget.selectedItem!["to_station"].toString();
print("Selected To CODE = $selectedTo");
}
// if (widget.selectedItem != null &&
// widget.selectedItem!["from_station"] != null) {
// String fromPlaceDisplay = widget.selectedItem!["from_station"].toString();
//
// selectedFrom = countryMap.entries
// .firstWhere((entry) => entry.value == fromPlaceDisplay,
// orElse: () => MapEntry('', '')) // avoid crash if not found
// .key;
//
// print("Selected From CODE = $selectedFrom");
// }
// if (widget.selectedItem != null &&
// widget.selectedItem!["to_station"] != null) {
// selectedClass = widget.selectedItem!["to_station"].toString();
// }
_trainNoController.addListener(() => _clearError("train_no"));
// _fromController.addListener(() => _clearError("from_station"));
_toController.addListener(() => _clearError("to_station"));
_dateController.addListener(() => _clearError("date"));
_timeController.addListener(() => _clearError("time"));
loadCountryList();
}
@override
void dispose() {
_trainNoFocusNode.dispose();
_dateFocusNode.dispose();
_timeFocusNode.dispose();
_typeOfClassFocusNode.dispose();
_fromFocusNode.dispose();
_toFocusNode.dispose();
_commentsFocusNode.dispose();
_trainNoController.dispose();
_hotelNameController.dispose();
_fromController.dispose();
_toController.dispose();
_dateController.dispose();
_timeController.dispose();
_trainCommentsController.dispose();
super.dispose();
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
void _clearError(String field) {
if (mounted && errorMessages.containsKey(field)) {
setState(() {
errorMessages.remove(field);
});
}
}
bool isValidData(Map<String, dynamic> data) {
errorMessages.clear(); // Reset errors
// Required fields that must not be empty
List<String> requiredFields = [
"train_no",
"class",
"from_station",
"to_station",
"date",
"time",
];
// Check validation for each field
for (String field in requiredFields) {
if (data[field] == null || data[field].toString().trim().isEmpty) {
errorMessages[field] = "Required";
}
}
if (selectedFrom == selectedTo) {
errorMessages["to_station"] = "Change Destination";
}
print("Error: to_place -> Change Destination (selectedFrom == selectedTo)");
return errorMessages.isEmpty; // Valid if there are no errors
}
Future<void> loadCountryList() async {
setState(() {
isCountryLoading = true;
});
final result = await apiService.fetchTrainCountryList();
print("ResultCountry : $result");
// Create a map: Country_Code -> "City, Airport"
Map<String, String> tempCountryMap = {};
for (var country in result) {
String displayName = '${country['Station_Name']} ';
tempCountryMap[country['Station_Code']] = displayName;
}
setState(() {
countryMap = tempCountryMap; // Update the map
isCountryLoading = false;
});
}
void handleSave() {
print("Handle Save accomadationData $trainData");
Map<String, dynamic> data = trainData;
if (!isValidData(data)) {
print("Validation Failed: Required fields are missing.");
setState(() {});
return; // Stop execution if validation fails
} else {
widget.onSavetrain(trainData);
}
widget.onClose("Train", false);
// widget.onClose(false); // Close screen after saving
}
@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("Train Booking List",
// style: TextStyle(
// fontSize: 18,
// fontWeight: FontWeight.bold,
// color: Color(0xFF575A74))),
// SizedBox(
// height: 6,
// ),
Padding(
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
),
],
),
),
),
);
},
);
}
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),
_buildSecondRow(isDesktop),
];
return [
...buildResponsiveRow(_buildFirstRow(isDesktop)),
// Iterate over rowBuilders and wrap each in a responsive container
...rowBuilders.expand((row) => buildResponsiveRow(row)),
...buildResponsiveRow(_buildThirdRow(isDesktop)),
// Actions row remains a Row
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Train Number *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
isDesktop
? Row(children: _buildTripType(isDesktop))
: Column(children: _buildTripType(isDesktop)),
if (errorMessages["train_no"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
],
],
),
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),
),
),
);
}
// Default selected value
String? selectedPurpose =
dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
CustomTextFieldItnerarySubWrapper(
isFocused: _trainNoFocused,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _trainNoFocusNode,
controller: _trainNoController,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "Train number",
labelStyle: TextStyle(fontSize: 12, color: Colors.grey),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
),
),
),
),
];
}
List<Widget> _builClassType(bool isDesktop) {
return [];
}
List<Widget> _buildSecondRow(bool isDesktop) {
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),
initialEntryMode: DatePickerEntryMode.calendarOnly,
);
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_dateController.text = DateFormat('dd-MM-yyyy').format(pickedDate);
});
}
}
Future<void> _selectCheckOutTime(BuildContext context) async {
TimeOfDay? pickedTime = await showTimePicker(
context: context,
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null) {
final now = DateTime.now();
// Parse the selected date
final dateText = _dateController.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"] = "You can't select a past time.";
});
return;
}
// ✅ Valid time selection
setState(() {
_selectedCheckOutTime = pickedTime;
final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
_timeController.text = formattedTime;
// errorMessages["time_$index"] = ""; // clear previous error
errorMessages.remove("time");
});
}
// 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;
// });
// }
}
//----------------------------------------------
List<dynamic> purposeList = widget.apiDataForClass?['train_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
// selectedClass ??=
// dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Class *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
// CustomTextFieldItnerarySubWrapper(
// isFocused: _isHotelNameFocused,
// isDesktop: isDesktop,
// // width: isDesktop
// // ? MediaQuery.of(context).size.width * 0.34
// // : MediaQuery.of(context).size.width * 0.66,
// child: SizedBox(
// height: 40,
// child: DropdownButtonFormField<String>(
// focusNode: _hotelNameFocusNode, // Assign the correct focus node
// // controller: _hotelNameController,
// value: selectedClass,
// style: TextStyle(fontSize: 12),
// decoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ), // Proper padding
// ),
// onChanged:
// purposeList.isNotEmpty
// ? (newValue) {
// setState(() {
// selectedClass = newValue;
// });
// }
// : null,
// items: dropdownItems,
// ),
// ),
// ),
// CustomTextFieldWrapper
CustomTextFieldItnerarySubWrapper(
isFocused: _typeOfClassFocus,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
child: SizedBox(
height: 40,
width: double.infinity,
child: Focus(
focusNode: _typeOfClassFocusNode,
onFocusChange: (hasFocus) {
setState(() {
_typeOfClassFocus = hasFocus;
});
},
child: GestureDetector(
onTap: () {
_typeOfClassFocusNode?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
key:
selectedClass == null
? UniqueKey()
: ValueKey(selectedClass),
items: purposeList.cast<Map<String, dynamic>>(),
// selectedItem: purposeList.firstWhere(
// (item) => item['dropdown_key'] == selectedClass,
// orElse: () => {},
// ),
selectedItem:
selectedClass != null
? purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedClass,
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: 5,
),
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(5),
borderSide: BorderSide(
color:
(_typeOfClassFocus ?? false)
? layoutColor!
: Colors.white,
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(_typeOfClassFocus ?? false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 1,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor, width: 1),
),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
return Text(
"Select ",
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<String, dynamic>? newValue) async {
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 = "1";
selectedClass = newValue['dropdown_key'];
});
print("Selected Purpose: ${selectedClass}");
} else {
setState(() {
exceptionalClass = "0";
selectedClass =
null; // ❌ Clear selection if user cancels
});
print(
"selectedItem resolved to: ${selectedClass}",
);
}
return;
} else if (newValue != null &&
newValue['is_allowed'] == 'yes') {
setState(() {
exceptionalClass = "0";
selectedClass = newValue?['dropdown_key'];
print("Selected Purpose: ${selectedClass}");
});
}
// if (newValue != null &&
// newValue['is_allowed'] == 'No') {
// print('not allowed');
//
// setState(() {
// exceptionalClass = "1";
// // hasExceptionalClass = true;
// });
// }
setState(() {
selectedClass = newValue?['dropdown_key'];
print("Selected Purpose: ${selectedClass}");
});
}
: null,
),),)
),
),
if (errorMessages["class"] != 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(
"From *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
// CustomTextFieldItnerarySubWrapper(
// isFocused: _fromFocus,
// isDesktop: isDesktop,
// child: SizedBox(
// height: 40,
// child:
// isCountryLoading
// ? Center(child: CircularProgressIndicator())
// : DropdownSearch<String>(
// // selectedItem: selectedFrom != null
// // ? countryMap[selectedFrom]
// // : null,
// selectedItem:
// selectedFrom != null
// ? countryMap[selectedFrom] // get the display value from code
// : null,
// popupProps: PopupProps.menu(
// menuProps: MenuProps(backgroundColor: Colors.white),
// constraints: BoxConstraints(maxHeight: 230),
// showSearchBox: true,
// searchFieldProps: TextFieldProps(
// decoration: InputDecoration(
// hintText: "Search ...",
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ),
// ),
// ),
// ),
//
// items: countryMap.values.toList(),
//
// dropdownDecoratorProps: DropDownDecoratorProps(
// dropdownSearchDecoration: InputDecoration(
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// vertical: 5,
// ),
// border: InputBorder.none,
// ),
// ),
// 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 = countryMap.entries
// // .firstWhere((entry) => entry.value == newValue)
// // .key;
// //
// // print(selectedFrom);
// // });
// // },
// onChanged: (String? newValue) {
// setState(() {
// selectedFrom =
// countryMap.entries
// .firstWhere(
// (entry) => entry.value == newValue,
// )
// .key;
// });
// },
// ),
// ),
// // child: SizedBox(
// // height: 40,
// // child: TextField(
// // focusNode: _fromFocusNode,
// // controller: _fromController,
// // 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),
// // ),
// // ),
// // ),
// ),
CustomTextFieldItnerarySubWrapper(
isFocused: _fromFocus ?? false,
padding: const EdgeInsets.symmetric(horizontal: 0),
isDesktop: isDesktop,
child: SizedBox(
height: 40,
child: Focus(
focusNode: _fromFocusNode,
onFocusChange: (hasFocus) {
setState(() {
_fromFocus = hasFocus;
});
},
child: GestureDetector(
onTap: () {
_fromFocusNode?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedFrom],
popupProps: PopupProps.menu(
showSearchBox: true,
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 150),// maxHeight: 250
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
borderSide: BorderSide(
color:
(_fromFocus ?? false)
? layoutColor!
: Colors.white,
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(_fromFocus ?? false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 1,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor, width: 1),
),
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
),
onChanged: (String? newValue) {
setState(() {
selectedFrom =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
});
},
),),)
),
),
if (errorMessages["from_station"] != 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: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
// CustomTextFieldItnerarySubWrapper(
// isFocused: _toFocus,
// isDesktop: isDesktop,
// child: SizedBox(
// height: 40,
// child:
// isCountryLoading
// ? Center(child: CircularProgressIndicator())
// : DropdownSearch<String>(
// selectedItem:
// selectedTo != null
// ? countryMap[selectedTo] // get the display value from code
// : null,
// popupProps: PopupProps.menu(
// menuProps: MenuProps(backgroundColor: Colors.white),
// constraints: BoxConstraints(maxHeight: 230),
// showSearchBox: true,
// searchFieldProps: TextFieldProps(
// decoration: InputDecoration(
// hintText: "Search ...",
// contentPadding: EdgeInsets.symmetric(
// horizontal: 10,
// ),
// ),
// ),
// ),
// items: countryMap.values.toList(),
// dropdownDecoratorProps: DropDownDecoratorProps(
// dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
// ),
// ),
// dropdownBuilder:
// (context, selectedItem) => Align(
// alignment: Alignment.centerLeft,
// child: Text(
// selectedItem ?? "Select",
// style: TextStyle(fontSize: 12),
// ),
// ),
// onChanged: (String? newValue) {
// setState(() {
// selectedTo =
// countryMap.entries
// .firstWhere(
// (entry) => entry.value == newValue,
// )
// .key;
// });
// },
// ),
// ),
// ),
CustomTextFieldItnerarySubWrapper(
isFocused: _toFocus ?? false,
isDesktop: isDesktop,
padding: const EdgeInsets.symmetric(horizontal: 0),
child: SizedBox(
height: 40,
child: Focus(
focusNode: _toFocusNode,
onFocusChange: (hasFocus) {
setState(() {
_toFocus = hasFocus;
});
},
child: GestureDetector(
onTap: () {
_toFocusNode?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: countryMap[selectedTo],
popupProps: PopupProps.menu(
showSearchBox: true,
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 150),
menuProps: const MenuProps(backgroundColor: Colors.white),
itemBuilder:
(context, item, isSelected) => Container(
color: Colors.white,
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 6,
),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 11.5),
),
),
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search ...",
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
),
items: countryMap.values.toList(),
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
// border: InputBorder.none,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5),
borderSide: BorderSide(
color:
(_toFocus ?? false)
? layoutColor!
: Colors.white,
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color:
(_toFocus ?? false)
? layoutColor
: Colors.white,
// : const Color(0xFFD6D5E6),
width: 1,
// const Color(0xFFD6D5E6),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: layoutColor, width: 1),
),
contentPadding: EdgeInsets.symmetric(horizontal: 10),
),
),
dropdownBuilder:
(context, selectedItem) => Align(
alignment: Alignment.centerLeft,
child: Text(
selectedItem ?? "Select ",
style: GoogleFonts.poppins(
fontSize: 12,
color: Colors.black,
),
),
),
onChanged: (String? newValue) {
setState(() {
selectedTo =
countryMap.entries
.firstWhere((entry) => entry.value == newValue)
.key;
});
},
),),),
),
),
if (errorMessages["to_station"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["to_station"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
// onTap: () => _selectCheckOutDate(context),
onTap: () {
_dateFocusNode.requestFocus();
_selectCheckOutDate(context);
},
child: AbsorbPointer(
child: TextField(
focusNode: _dateFocusNode,
controller: _dateController,
readOnly: true, // Prevents typing
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"] != 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: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _timeFocus,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () {
_timeController.text = "";
_timeFocusNode.requestFocus();
_selectCheckOutTime(context);
},
child: AbsorbPointer(
child: TextField(
focusNode: _timeFocusNode,
controller: _timeController,
readOnly: true,
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"] != null) ...[
SizedBox(height: 5), // Space before error message
Text(
errorMessages["time"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
];
}
List<Widget> _buildThirdRow(bool isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Comments",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.32 : null,
child: SizedBox(
height: 40,
child: TextField(
focusNode: _commentsFocusNode,
controller: _trainCommentsController,
// 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: 16),
),
),
),
),
],
),
if (isDesktop) Spacer(),
SizedBox(height: 5),
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: _handleAction(isDesktop),
),
],
),
];
}
List<Widget> _handleAction(bool isDesktop) {
return [
// Close Button
ElevatedButton(
onPressed: () {
// widget.onClose(false);
widget.onClose("Train", false);
},
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),
),
),
];
}
}