Current Time Validations

This commit is contained in:
venbaittech 2025-06-09 12:16:02 +05:30
parent 5ccb201b7a
commit de960412a0
7 changed files with 679 additions and 504 deletions

View File

@ -15,13 +15,14 @@ class AccomodationScreen extends StatefulWidget {
final String? loginUser;
final String? tripType;
AccomodationScreen(
{required this.onClose,
required this.onSaveAccomadation,
required this.selectedItem,
required this.loginUser,
required this.flightData,
this.tripType});
AccomodationScreen({
required this.onClose,
required this.onSaveAccomadation,
required this.selectedItem,
required this.loginUser,
required this.flightData,
this.tripType,
});
@override
_AccomodationScreenState createState() => _AccomodationScreenState();
@ -107,15 +108,23 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
super.initState();
_addFocusListener(
_destinationFocusNode, (focus) => _destinationFocused = focus);
_destinationFocusNode,
(focus) => _destinationFocused = focus,
);
_addFocusListener(
_hotelNameFocusNode, (focus) => _isHotelNameFocused = focus);
_hotelNameFocusNode,
(focus) => _isHotelNameFocused = focus,
);
_addFocusListener(_checkInFocusNode, (focus) => _checkInFocus = focus);
_addFocusListener(
_checkInTimeFocusNode, (focus) => _checkInTimeFocus = focus);
_checkInTimeFocusNode,
(focus) => _checkInTimeFocus = focus,
);
_addFocusListener(_checkOutFocusNode, (focus) => _checkOutFocus = focus);
_addFocusListener(
_checkOutTimeFocusNode, (focus) => _checkOutTimeFocus = focus);
_checkOutTimeFocusNode,
(focus) => _checkOutTimeFocus = focus,
);
_addFocusListener(_commentsFocusNode, (focus) => _commentsFocus = focus);
_destinationController = initController("destination_city");
@ -149,25 +158,29 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
flightFirstToDestinationNotifier.value = result['firstToDestination'];
print(
"flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value ");
"flightfirstToDestinationNotifier: $flightFirstToDestinationNotifier.value ",
);
// Only set controller after value is updated
// final parsedDate = DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat("dd-MM-yyyy")
.parse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat(
"dd-MM-yyyy",
).parse(flightFirstTripDateNotifier.value ?? '');
if (parsedDate != null) {
_checkInController.text = DateFormat('dd-MM-yyyy').format(parsedDate);
}
// final parsedEndDate = DateTime.tryParse(flightLastTripDateNotifier.value ?? '');
final parsedEndDate = DateFormat("dd-MM-yyyy")
.parse(flightLastTripDateNotifier.value ?? '');
final parsedEndDate = DateFormat(
"dd-MM-yyyy",
).parse(flightLastTripDateNotifier.value ?? '');
if (parsedEndDate != null) {
// _checkOutController.text = DateFormat('dd-MM-yyyy').format(parsedEndDate);
_checkOutController.text =
DateFormat('dd-MM-yyyy').format(parsedEndDate);
_checkOutController.text = DateFormat(
'dd-MM-yyyy',
).format(parsedEndDate);
}
// Check and set default times if empty
@ -202,17 +215,16 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
}
Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) {
final allTrips = flightData
.expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>()
.toList();
List<Map<String, dynamic>> flightData,
) {
final allTrips =
flightData
.expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>()
.toList();
if (allTrips.isEmpty) {
return {
'firstTripDate': null,
'lastTripDate': null,
};
return {'firstTripDate': null, 'lastTripDate': null};
}
allTrips.sort((a, b) {
@ -230,7 +242,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
return {
'firstTripDate': firstTrip['date'],
'lastTripDate': lastTrip['date'],
'firstToDestination': firstTripToDestination['to_place']
'firstToDestination': firstTripToDestination['to_place'],
};
}
@ -244,7 +256,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
"checkin_date",
"checkin_time",
"checkout_date",
"checkout_time"
"checkout_time",
];
// Check validation for each field
@ -284,8 +296,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
checkOutTime.toString().isNotEmpty) {
try {
final checkInDateTime = formatTime.parse("$checkIn $checkInTime");
final checkOutDateTime =
formatTime.parse("$checkOut $checkOutTime");
final checkOutDateTime = formatTime.parse(
"$checkOut $checkOutTime",
);
if (!checkOutDateTime.isAfter(checkInDateTime)) {
errorMessages["checkout_time"] =
@ -359,60 +372,51 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
@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),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
return Container(
// color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
),
)
],
],
),
),
),
),
);
});
);
},
);
}
List<Widget> _buildAccomadtionForm(isDesktop) {
return [
isDesktop
? Row(
children: _buildFirstRow(isDesktop),
)
: Column(
children: _buildFirstRow(isDesktop),
),
? Row(children: _buildFirstRow(isDesktop))
: Column(children: _buildFirstRow(isDesktop)),
SizedBox(height: 10), // Spacing between first and second row
isDesktop
? Row(
children: _buildSecondRow(isDesktop),
)
: Column(
children: _buildSecondRow(isDesktop),
),
? Row(children: _buildSecondRow(isDesktop))
: Column(children: _buildSecondRow(isDesktop)),
SizedBox(height: 10),
isDesktop
? Row(
children: _buildThirdRow(isDesktop),
)
: Column(
children: _buildThirdRow(isDesktop),
),
? Row(children: _buildThirdRow(isDesktop))
: Column(children: _buildThirdRow(isDesktop)),
];
}
@ -432,24 +436,25 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// ),
// ],
// ),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Destination *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _destinationFocused,
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: TextField(
@ -475,29 +480,26 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Hotel Name *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
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: TextField(
@ -583,18 +585,61 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
initialTime: _selectedCheckInTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckInTime) {
if (pickedTime != null) {
final now = DateTime.now();
// Parse the selected date
final dateText = _checkInController.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["checkin_time"] = "You can't select a past time.";
});
return;
}
// Valid time selection
setState(() {
_selectedCheckInTime = 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),
);
final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
_checkInTimeController.text = formattedTime;
// errorMessages["time_$index"] = ""; // clear previous error
errorMessages.remove("checkin_time");
});
}
// if (pickedTime != null && pickedTime != _selectedCheckInTime) {
// setState(() {
// _selectedCheckInTime = 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),
// );
// _checkInTimeController.text = formattedTime;
// });
// }
}
//-------------------------------Check-In End
@ -647,10 +692,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// : firstDate;
DateTime firstDate = checkInDate;
DateTime initialDate = _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(firstDate)
? _selectedCheckOutDate!
: firstDate;
DateTime initialDate =
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(firstDate)
? _selectedCheckOutDate!
: firstDate;
final pickedDate = await showDatePicker(
context: context,
@ -662,8 +708,9 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_checkOutController.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
_checkOutController.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
errorMessages.remove("checkout_date");
});
}
@ -681,8 +728,13 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
// 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,
),
);
_checkOutTimeController.text = formattedTime;
errorMessages.remove("checkout_time");
@ -697,9 +749,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
Text(
"Check-in *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -720,8 +773,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
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,
),
),
),
),
@ -737,21 +793,17 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time (Check-in) *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -760,7 +812,10 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckInTime(context),
onTap: () {
_checkInTimeController.text = "";
_selectCheckInTime(context);
},
child: AbsorbPointer(
child: TextField(
focusNode: _checkInTimeFocusNode,
@ -772,8 +827,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
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,
),
),
),
),
@ -789,21 +847,17 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Check-out*",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -824,8 +878,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
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,
),
),
),
),
@ -841,21 +898,17 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Time (Check-out) *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
@ -876,8 +929,11 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
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,
),
),
),
),
@ -904,17 +960,19 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
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(
isFocused: _commentsFocus, // Dropdown doesn't use focus
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: TextField(
@ -934,9 +992,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
],
),
if (isDesktop) Spacer(),
SizedBox(
height: 5,
),
SizedBox(height: 5),
Column(
children: [
Row(
@ -957,9 +1013,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
},
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(
@ -968,7 +1022,6 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
@ -976,9 +1029,7 @@ class _AccomodationScreenState extends State<AccomodationScreen> {
},
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(

View File

@ -568,9 +568,9 @@ class FlightScreenState extends State<FlightScreen> {
}
bool validateFields() {
errorMessages.clear(); // Reset errors
// errorMessages.clear(); // Reset errors
rowCount = 1; // Default row count for One-way
rowCount = 1; // Default row count for One-way
if (selectedTripType == "Roundtrip") {
rowCount = 2; // Fixed for Roundtrip
} else if (selectedTripType == "Multitrip") {
@ -594,23 +594,21 @@ class FlightScreenState extends State<FlightScreen> {
// errorMessages["to_place_$i"] = "Change Destination";
// }
if (selectedTo[i] == null) {
errorMessages["to_place_$i"] = "Required";
print("Error: to_place_$i -> Required (selectedTo[$i] is null)");
} else if (selectedFrom[i] == selectedTo[i]) {
if(selectedTripType == "Roundtrip"){
if (selectedTripType == "Roundtrip") {
errorMessages["to_place_1"] = "Change Destination";
}
else{
} else {
errorMessages["to_place_$i"] = "Change Destination";
}
print("Error: to_place_$i -> Change Destination (selectedFrom[$i] == selectedTo[$i])");
print(
"Error: to_place_$i -> Change Destination (selectedFrom[$i] == selectedTo[$i])",
);
}
// if (textControllers["_to${i}Controller"]?.text.trim().isEmpty ?? true) {
// errorMessages["to_place_$i"] = "Required";
// }
@ -620,13 +618,8 @@ class FlightScreenState extends State<FlightScreen> {
if (textControllers["_time${i}Controller"]?.text.trim().isEmpty ?? true) {
errorMessages["time_$i"] = "Required";
}
}
setState(() {}); // Update UI to show error messages
return errorMessages
@ -1238,41 +1231,73 @@ class FlightScreenState extends State<FlightScreen> {
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
if (pickedTime != null) {
final now = DateTime.now();
// Parse the selected date
final dateText = textControllers["_date${index}Controller"]?.text ?? "";
final selectedDate = DateFormat(
'dd-MM-yyyy',
).parse(dateText); // or 'yyyy-MM-dd' depending on your format
final selectedDateTime = DateTime(
selectedDate.year,
selectedDate.month,
selectedDate.day,
pickedTime.hour,
pickedTime.minute,
);
// Only validate past time if date is today
final isToday =
selectedDate.year == now.year &&
selectedDate.month == now.month &&
selectedDate.day == now.day;
bool isPastTime = selectedDateTime.isBefore(now);
if (isToday && isPastTime) {
setState(() {
errorMessages["time_$index"] = "You can't select a past time.";
});
return;
}
// Valid time selection
setState(() {
_selectedCheckOutTime = pickedTime;
// Formatting time to HH:mm (24-hour format)
final now = DateTime.now();
final formattedTime = DateFormat('HH:mm').format(
DateTime(
now.year,
now.month,
now.day,
pickedTime.hour,
pickedTime.minute,
),
);
// _timeController.text = formattedTime;
textControllers["_time${index}Controller"]?.text = formattedTime;
onPicked();
final formattedTime = DateFormat('HH:mm').format(selectedDateTime);
textControllers["_time${index}Controller"]?.text = formattedTime;
// errorMessages["time_$index"] = ""; // clear previous error
errorMessages.remove("time_$index");
onPicked(); // Trigger callback
});
}
// if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
// setState(() {
// _selectedCheckOutTime = pickedTime;
// // Formatting time to HH:mm (24-hour format)
// final now = DateTime.now();
// final formattedTime = DateFormat('HH:mm').format(
// DateTime(
// now.year,
// now.month,
// now.day,
// pickedTime.hour,
// pickedTime.minute,
// ),
// );
//
// // _timeController.text = formattedTime;
// textControllers["_time${index}Controller"]?.text = formattedTime;
//
// onPicked();
// });
// }
}
// late Map<String, String> countryMap; // Mapping country_code -> country_name
// late List<String> countryCodes; // List of country codes
// countryMap = {
// for (var item in countryList)
// if (item['country_code'] != null && item['country_name'] != null)
// item['country_code'] as String: item['country_name'] as String
// };
// // Extract only country codes for processing
// countryCodes = countryMap.keys.toList();
//
// selectedCountry ??= null;
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -1297,7 +1322,8 @@ class FlightScreenState extends State<FlightScreen> {
isCountryLoading
? Center(child: CircularProgressIndicator())
: DropdownSearch<String>(
enabled: !(selectedTripType == "Roundtrip" && index == 2),
enabled:
!(selectedTripType == "Roundtrip" && index == 2),
selectedItem:
selectedFrom[index] != null
? countryMap[selectedFrom[index]]
@ -1323,25 +1349,37 @@ class FlightScreenState extends State<FlightScreen> {
final airport = parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)"
final cityMatch = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode);
final cityMatch = RegExp(
r'^(.*)\s+\(([^)]+)\)$',
).firstMatch(cityAndCode);
final city = cityMatch?.group(1) ?? '';
final code = cityMatch?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0),
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
city,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
Text(
code,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
@ -1380,26 +1418,31 @@ class FlightScreenState extends State<FlightScreen> {
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null) {
return Align(
alignment: Alignment.centerLeft,child: const Text("Select", style: TextStyle(fontSize: 12)));
}
final parts = selectedItem.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
return Align(
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null) {
return Align(
alignment: Alignment.centerLeft,
child: Text(
cityAndCode ?? "Select",
style: const TextStyle(fontSize: 12),
),
);
},
child: const Text(
"Select",
style: TextStyle(fontSize: 12),
),
);
}
// dropdownBuilder:
final parts = selectedItem.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
return Align(
alignment: Alignment.centerLeft,
child: Text(
cityAndCode ?? "Select",
style: const TextStyle(fontSize: 12),
),
);
},
// dropdownBuilder:
// (context, selectedItem) => Align(
// alignment: Alignment.centerLeft,
// child: Text(
@ -1409,6 +1452,7 @@ class FlightScreenState extends State<FlightScreen> {
// ),
onChanged: (String? newValue) {
setState(() {
errorMessages.remove("from_place_$index");
// selectedFrom[index] = countryMap.entries
// .firstWhere((entry) => entry.value == newValue)
// .key;
@ -1420,13 +1464,11 @@ class FlightScreenState extends State<FlightScreen> {
)
.key;
if(selectedTripType == "Roundtrip"){
if (selectedTripType == "Roundtrip") {
print('rounfTo${selectedFrom[index]}');
// textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!;
selectedTo[2]= selectedFrom[index]!;
selectedTo[2] = selectedFrom[index]!;
}
});
},
),
@ -1477,7 +1519,8 @@ class FlightScreenState extends State<FlightScreen> {
isCountryLoading
? Center(child: CircularProgressIndicator())
: DropdownSearch<String>(
enabled: !(selectedTripType == "Roundtrip" && index == 2),
enabled:
!(selectedTripType == "Roundtrip" && index == 2),
selectedItem:
selectedTo[index] != null
? countryMap[selectedTo[index]]
@ -1502,27 +1545,38 @@ class FlightScreenState extends State<FlightScreen> {
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)"
final cityMatch = RegExp(r'^(.*)\s+\(([^)]+)\)$').firstMatch(cityAndCode);
final cityMatch = RegExp(
r'^(.*)\s+\(([^)]+)\)$',
).firstMatch(cityAndCode);
final city = cityMatch?.group(1) ?? '';
final code = cityMatch?.group(2) ?? '';
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0),
padding: const EdgeInsets.symmetric(
horizontal: 8.0,
vertical: 6.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
city,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
Text(
code,
style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold),
style: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
],
),
@ -1560,6 +1614,7 @@ class FlightScreenState extends State<FlightScreen> {
contentPadding: EdgeInsets.symmetric(horizontal: 1),
),
),
// dropdownBuilder:
// (context, selectedItem) => Align(
// alignment: Alignment.centerLeft,
@ -1568,26 +1623,30 @@ class FlightScreenState extends State<FlightScreen> {
// style: TextStyle(fontSize: 12),
// ),
// ),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null) {
return Align(
alignment: Alignment.centerLeft,
child: const Text(
"Select",
style: TextStyle(fontSize: 12),
),
);
}
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null) {
return Align(
alignment: Alignment.centerLeft,child: const Text("Select", style: TextStyle(fontSize: 12)));
}
final parts = selectedItem.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
final parts = selectedItem.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
return Align(
alignment: Alignment.centerLeft,
child: Text(
cityAndCode ?? "Select",
style: const TextStyle(fontSize: 12),
),
);
},
onChanged: (String? newValue) {
return Align(
alignment: Alignment.centerLeft,
child: Text(
cityAndCode ?? "Select",
style: const TextStyle(fontSize: 12),
),
);
},
onChanged: (String? newValue) {
setState(() {
errorMessages.remove("to_place_$index");
selectedTo[index] =
@ -1599,11 +1658,10 @@ class FlightScreenState extends State<FlightScreen> {
print(selectedTo[index]);
if(selectedTripType == "Roundtrip"){
if (selectedTripType == "Roundtrip") {
print('rounfTo${selectedTo[index]}');
// textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!;
selectedFrom[2]= selectedTo[index]!;
selectedFrom[2] = selectedTo[index]!;
}
});
},
@ -1612,7 +1670,10 @@ class FlightScreenState extends State<FlightScreen> {
),
if (errorMessages["to_place_$index"] != null) ...[
SizedBox(height: 5), // Space before error message
Text( errorMessages["to_place_$index"]! , style: TextStyle(color: Colors.red, fontSize: 12)),
Text(
errorMessages["to_place_$index"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),
@ -1835,6 +1896,7 @@ class FlightScreenState extends State<FlightScreen> {
height: 40,
child: GestureDetector(
onTap: () {
textControllers["_time${index}Controller"]?.text = "";
_clearError("time_$index");
// validateTimeDifference(index);
// _selectCheckOutTime(context);

View File

@ -15,13 +15,14 @@ class InsuranceScreen extends StatefulWidget {
final Map<String, dynamic>? selectedItem;
final String? loginUser;
InsuranceScreen(
{required this.onClose,
required this.apiData,
required this.onSaveInsurance,
required this.selectedItem,
required this.loginUser,
required this.flightData});
InsuranceScreen({
required this.onClose,
required this.apiData,
required this.onSaveInsurance,
required this.selectedItem,
required this.loginUser,
required this.flightData,
});
@override
_InsuranceScreenState createState() => _InsuranceScreenState();
@ -45,7 +46,8 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
late TextEditingController _tripTypeController = TextEditingController();
late TextEditingController _startdateController = TextEditingController();
late TextEditingController _endDateController = TextEditingController();
late TextEditingController _insuranceCommentsController = TextEditingController();
late TextEditingController _insuranceCommentsController =
TextEditingController();
late TextEditingController _nomineeController = TextEditingController();
bool _isHotelNameFocused = false;
@ -64,7 +66,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
"start_date": _startdateController.text,
"end_date": _endDateController.text,
"comments": _insuranceCommentsController.text,
"nominee_name" : _nomineeController.text,
"nominee_name": _nomineeController.text,
"created_by": widget.loginUser,
"updated_by": widget.loginUser,
};
@ -108,14 +110,18 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
});
});
_insuranceCommentsController =
TextEditingController(text: widget.selectedItem?["comments"] ?? "");
_startdateController =
TextEditingController(text: widget.selectedItem?["start_date"] ?? "");
_endDateController =
TextEditingController(text: widget.selectedItem?['end_date'] ?? "");
_nomineeController =
TextEditingController(text: widget.selectedItem?['nominee_name'] ?? "");
_insuranceCommentsController = TextEditingController(
text: widget.selectedItem?["comments"] ?? "",
);
_startdateController = TextEditingController(
text: widget.selectedItem?["start_date"] ?? "",
);
_endDateController = TextEditingController(
text: widget.selectedItem?['end_date'] ?? "",
);
_nomineeController = TextEditingController(
text: widget.selectedItem?['nominee_name'] ?? "",
);
// Set the selected value if available
if (widget.selectedItem != null &&
@ -135,17 +141,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// Only set controller after value is updated
// final parsedDate =
// DateTime.tryParse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat("dd-MM-yyyy")
.parse(flightFirstTripDateNotifier.value ?? '');
final parsedDate = DateFormat(
"dd-MM-yyyy",
).parse(flightFirstTripDateNotifier.value ?? '');
if (parsedDate != null) {
_startdateController.text = DateFormat('dd-MM-yyyy').format(parsedDate);
}
final parsedEndDate = DateFormat("dd-MM-yyyy")
.parse(flightLastTripDateNotifier.value ?? '');
final parsedEndDate = DateFormat(
"dd-MM-yyyy",
).parse(flightLastTripDateNotifier.value ?? '');
if (parsedEndDate != null) {
_endDateController.text =
DateFormat('dd-MM-yyyy').format(parsedEndDate);
_endDateController.text = DateFormat(
'dd-MM-yyyy',
).format(parsedEndDate);
print("parsedDate");
print(parsedDate);
@ -156,17 +165,16 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}
Map<String, String?> getFlightTripDateRange(
List<Map<String, dynamic>> flightData) {
final allTrips = flightData
.expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>()
.toList();
List<Map<String, dynamic>> flightData,
) {
final allTrips =
flightData
.expand((flight) => flight['trips'] ?? [])
.whereType<Map<String, dynamic>>()
.toList();
if (allTrips.isEmpty) {
return {
'firstTripDate': null,
'lastTripDate': null,
};
return {'firstTripDate': null, 'lastTripDate': null};
}
allTrips.sort((a, b) {
@ -199,7 +207,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
List<String> requiredFields = [
"type_of_insurance",
"start_date",
"end_date"
"end_date",
];
// Check validation for each field
@ -267,30 +275,33 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
@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),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(28.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
return Container(
// color: Color(0xFFF4F4FB),
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 30.0),
child: Center(
child: Column(children: _buildAccomadtionForm(isDesktop)),
),
),
)
],
],
),
),
),
),
);
});
);
},
);
}
List<Widget> _buildAccomadtionForm(bool isDesktop) {
@ -303,7 +314,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
List<List<Widget>> rowBuilders = [
// _builClassType(isDesktop),
_buildSecondRow(isDesktop)
_buildSecondRow(isDesktop),
];
return [
@ -318,51 +329,52 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
];
}
List<Widget> _buildFirstRow(isDesktop) {
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Insurance Type", // not in use
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
),
SizedBox(height: 5),
isDesktop
? Row(children: _buildTripType(isDesktop))
: Column(children: _buildTripType(isDesktop))
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
];
}
// List<Widget> _buildFirstRow(isDesktop) {
// return [
// Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// children: [
// Text(
// "Insurance Type", // not in use
// style: GoogleFonts.poppins(
// fontSize: 12,
// fontWeight: FontWeight.w500,
// 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?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'],
child: Text(item['dropdown_value']),
))
.toList();
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)),
child: Text(
"No options available",
style: TextStyle(color: Colors.grey),
),
),
);
}
@ -375,9 +387,10 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
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<String>(
@ -386,21 +399,23 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: InputDecoration(
border: InputBorder.none,
contentPadding:
EdgeInsets.symmetric(horizontal: 10), // Proper padding
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
), // Proper padding
),
onChanged: purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedInsuranceType = newValue;
if (selectedInsuranceType!.isNotEmpty) {
errorMessages.remove("type_of_insurance");
}
});
onChanged:
purposeList.isNotEmpty
? (newValue) {
setState(() {
selectedInsuranceType = newValue;
if (selectedInsuranceType!.isNotEmpty) {
errorMessages.remove("type_of_insurance");
}
});
print(selectedInsuranceType);
}
: null,
print(selectedInsuranceType);
}
: null,
items: dropdownItems,
),
@ -409,16 +424,17 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
];
}
List<Widget> _buildInsuranceTypeDropdown(bool isDesktop) {
// List<dynamic> purposeList = widget.apiData?['insurance_type_of_insurance'] ?? [];
List<Map<String, dynamic>> purposeList = (widget.apiData?['insurance_type_of_insurance'] as List<dynamic>?)
?.map((e) => Map<String, dynamic>.from(e as Map))
.toList() ?? [];
List<Map<String, dynamic>> purposeList =
(widget.apiData?['insurance_type_of_insurance'] as List<dynamic>?)
?.map((e) => Map<String, dynamic>.from(e as Map))
.toList() ??
[];
// Find selected item object based on key
Map<String, dynamic>? selectedItem = purposeList.firstWhere(
(item) => item['dropdown_key'] == selectedInsuranceType,
(item) => item['dropdown_key'] == selectedInsuranceType,
orElse: () => {},
);
@ -476,17 +492,20 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
},
selectedItem: selectedItem!.isNotEmpty ? selectedItem : null,
itemAsString: (item) => item['dropdown_value'] ?? '',
onChanged: purposeList.isNotEmpty
? (Map<String, dynamic>? newItem) {
if (newItem != null) {
setState(() {
selectedInsuranceType = newItem['dropdown_key'];
errorMessages.remove("type_of_insurance");
});
print("Selected Insurance Type: $selectedInsuranceType");
}
}
: null,
onChanged:
purposeList.isNotEmpty
? (Map<String, dynamic>? newItem) {
if (newItem != null) {
setState(() {
selectedInsuranceType = newItem['dropdown_key'];
errorMessages.remove("type_of_insurance");
});
print(
"Selected Insurance Type: $selectedInsuranceType",
);
}
}
: null,
items: purposeList,
),
),
@ -494,26 +513,29 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
];
}
List<Widget> _buildSecondRow(bool isDesktop) {
List<dynamic> purposeList =
widget.apiData?['insurance_type_of_insurance'] ?? [];
// selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null;
List<DropdownMenuItem<String>> dropdownItems = purposeList
.map((item) => DropdownMenuItem<String>(
value: item['dropdown_key'],
child: Text(item['dropdown_value']),
))
.toList();
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)),
child: Text(
"No options available",
style: TextStyle(color: Colors.grey),
),
),
);
}
@ -569,8 +591,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
setState(() {
_selectedCheckOutDate = pickedDate;
_startdateController.text =
DateFormat('dd-MM-yyyy').format(pickedDate);
_startdateController.text = DateFormat(
'dd-MM-yyyy',
).format(pickedDate);
});
}
}
@ -594,10 +617,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
// : firstDate;
DateTime firstDate = checkInDate;
DateTime initialDate = _selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(firstDate)
? _selectedCheckOutDate!
: firstDate;
DateTime initialDate =
_selectedCheckOutDate != null &&
_selectedCheckOutDate!.isAfter(firstDate)
? _selectedCheckOutDate!
: firstDate;
final pickedDate = await showDatePicker(
context: context,
@ -624,7 +648,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
}
}
return [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -643,7 +666,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
CustomTextFieldWrapper(
isFocused: _isHotelNameFocused,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.17 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null,
child: SizedBox(
height: 35,
width: double.infinity,
@ -653,53 +676,56 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
decoration: const InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
contentPadding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
),
hint: Text(
"Select ",
style: GoogleFonts.poppins(color: Colors.grey, fontSize: 13),
),
items: purposeList.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item['dropdown_key'],
child: Text(
item['dropdown_value'] ?? '',
style: GoogleFonts.poppins(fontSize: 12),
),
);
}).toList(),
onChanged: purposeList.isNotEmpty
? (String? newValue) {
setState(() {
selectedInsuranceType = newValue;
if ((selectedInsuranceType ?? '').isNotEmpty) {
errorMessages.remove("type_of_insurance");
}
});
print("Selected Insurance Type: $selectedInsuranceType");
}
: null,
items:
purposeList.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item['dropdown_key'],
child: Text(
item['dropdown_value'] ?? '',
style: GoogleFonts.poppins(fontSize: 12),
),
);
}).toList(),
onChanged:
purposeList.isNotEmpty
? (String? newValue) {
setState(() {
selectedInsuranceType = newValue;
if ((selectedInsuranceType ?? '').isNotEmpty) {
errorMessages.remove("type_of_insurance");
}
});
print(
"Selected Insurance Type: $selectedInsuranceType",
);
}
: null,
),
),
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Nominee",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
@ -726,27 +752,23 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
),
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Start Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
@ -754,8 +776,9 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
await _selectCheckOutDate(context);
if (_startdateController.text.isNotEmpty) {
setState(() {
errorMessages
.remove("start_date"); // Removes the key completely
errorMessages.remove(
"start_date",
); // Removes the key completely
});
}
},
@ -770,8 +793,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
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,
),
),
),
),
@ -780,34 +806,27 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
),
if (errorMessages["start_date"] != 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(
"End Date *",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _dateFocus,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null,
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox(
height: 40,
child: GestureDetector(
@ -843,8 +862,11 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
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,
),
),
),
),
@ -861,12 +883,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
],
],
),
if (isDesktop)
Spacer()
else
SizedBox(
height: 8,
),
if (isDesktop) Spacer() else SizedBox(height: 8),
];
}
@ -878,17 +895,19 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
Text(
"Comments",
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74)),
fontSize: 12,
fontWeight: FontWeight.w500,
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _commentsFocus, // Dropdown doesn't use focus
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.31
: MediaQuery.of(context).size.width * 0.66,
child: SizedBox(
height: 40,
child: TextField(
@ -908,9 +927,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
],
),
if (isDesktop) Spacer(),
SizedBox(
height: 5,
),
SizedBox(height: 5),
Column(
children: [
Row(
@ -931,9 +948,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
},
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(
@ -942,7 +957,6 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
),
),
SizedBox(width: 10), // Space between buttons
// Save Changes Button
ElevatedButton(
onPressed: () {
@ -950,9 +964,7 @@ class _InsuranceScreenState extends State<InsuranceScreen> {
},
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(

View File

@ -480,23 +480,66 @@ class _TrainScreenState extends State<TrainScreen> {
initialTime: _selectedCheckOutTime ?? TimeOfDay.now(),
);
if (pickedTime != null && pickedTime != _selectedCheckOutTime) {
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;
// 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,
),
);
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;
// });
// }
}
//----------------------------------------------
@ -831,7 +874,10 @@ class _TrainScreenState extends State<TrainScreen> {
child: SizedBox(
height: 40,
child: GestureDetector(
onTap: () => _selectCheckOutTime(context),
onTap: () {
_timeController.text = "";
_selectCheckOutTime(context);
},
child: AbsorbPointer(
child: TextField(
focusNode: _timeFocusNode,
@ -856,7 +902,10 @@ class _TrainScreenState extends State<TrainScreen> {
),
if (errorMessages["time"] != null) ...[
SizedBox(height: 5), // Space before error message
Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)),
Text(
errorMessages["time"]!,
style: TextStyle(color: Colors.red, fontSize: 12),
),
],
],
),

View File

@ -3008,8 +3008,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
labelText: "Trip Name *",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74),
fontWeight: FontWeight.w400,
color: Colors.grey,
// color: Color(0xFF575A74),
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,

View File

@ -21,10 +21,10 @@ packages:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.dev"
source: hosted
version: "2.12.0"
version: "2.13.0"
bcrypt:
dependency: "direct main"
description:
@ -149,10 +149,10 @@ packages:
dependency: transitive
description:
name: fake_async
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.2"
version: "1.3.3"
ffi:
dependency: transitive
description:
@ -465,18 +465,18 @@ packages:
dependency: "direct main"
description:
name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.19.0"
version: "0.20.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
url: "https://pub.dev"
source: hosted
version: "10.0.8"
version: "10.0.9"
leak_tracker_flutter_testing:
dependency: transitive
description:
@ -1014,10 +1014,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "14.3.1"
version: "15.0.0"
vsc_quill_delta_to_html:
dependency: "direct main"
description:

View File

@ -44,7 +44,7 @@ dependencies:
responsive_builder: ^0.7.1
shared_preferences: ^2.5.2
easy_stepper: ^0.8.5+1
intl: ^0.19.0
intl: ^0.20.2
dropdown_search: ^5.0.6
file_picker: ^10.0.0
bcrypt: ^1.1.3