4901 lines
166 KiB
Dart
4901 lines
166 KiB
Dart
// import 'dart:io' as html;
|
|
import 'dart:io' as io;
|
|
|
|
import 'package:dropdown_search/dropdown_search.dart';
|
|
import 'package:flutter/cupertino.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:frontend/utils/auth_utils.dart';
|
|
import 'package:google_fonts/google_fonts.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:universal_html/html.dart' as html;
|
|
|
|
import 'dart:async';
|
|
|
|
import 'dart:typed_data'; // Import for Uint8List
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
|
|
import '../../../config/apiUrl.dart';
|
|
import '../../../services/apiService.dart';
|
|
import '../../../widgets/custom_user_form.dart';
|
|
|
|
import '../../../widgets/custom_user_form.dart';
|
|
import '../../../widgets/custom_user_travel.dart';
|
|
|
|
class TravellerDetails extends StatefulWidget {
|
|
final bool isDesktop;
|
|
final Map<String, TextEditingController> controllers;
|
|
final Map<String, String> errorMessages;
|
|
|
|
final bool isViewMode;
|
|
final Map<String, dynamic>? travelDetails;
|
|
final String? passportFileUrlFromApi;
|
|
final String? userIdApi;
|
|
final String fullName;
|
|
|
|
const TravellerDetails({
|
|
Key? key,
|
|
required this.controllers,
|
|
required this.errorMessages,
|
|
required this.isDesktop,
|
|
required this.isViewMode,
|
|
this.travelDetails,
|
|
this.passportFileUrlFromApi,
|
|
this.userIdApi,
|
|
required this.fullName,
|
|
}) : super(key: key);
|
|
@override
|
|
TravellerDetailsState createState() => TravellerDetailsState();
|
|
}
|
|
|
|
class TravellerDetailsState extends State<TravellerDetails> {
|
|
final ApiService apiService = ApiService();
|
|
html.File? passportFile;
|
|
late List<bool> isExpandedList;
|
|
int? expandedIndex;
|
|
bool isCountryLoading = true;
|
|
|
|
Map<String, String> errorMessages = {};
|
|
|
|
late final userId;
|
|
String? selectedFileNames;
|
|
Uint8List? passportDocumentBytes;
|
|
String? passportFileUrlFromApi;
|
|
String? _selectedTripType;
|
|
|
|
// String? selectedPurpose;
|
|
// String? selectedCountry;
|
|
String? selectedLocalIdType;
|
|
List<dynamic> countryList = [];
|
|
|
|
String? selectedDomesticSeat;
|
|
String? selectedIntenationalSeat;
|
|
String? SelectedIdType;
|
|
|
|
Map<int, TextEditingController> airlineControllers = {};
|
|
Map<int, TextEditingController> flierNumberControllers = {};
|
|
|
|
List<Map<String, dynamic>> hotelLoyaltyEntries = [];
|
|
int _rowhotelCounter = 1;
|
|
|
|
List<Map<String, dynamic>> frequentFlierEntries = [];
|
|
int _rowFlierCounter = 1;
|
|
|
|
List<Map<String, dynamic>> visaEntries = [];
|
|
int _rowVisaCounter = 1;
|
|
|
|
Map<String, String> countryMap = {};
|
|
late List<dynamic>? apiCountryData;
|
|
late List<dynamic>? apiHotelsData;
|
|
late List<dynamic>? apiAirlineCountryData;
|
|
Map<String, dynamic>? apiData;
|
|
final Map<String, TextEditingController> controllers = {};
|
|
Map<String, FocusNode> focusNodes = {};
|
|
Map<String, bool> focusStates = {};
|
|
|
|
bool _iSeatPrefFocus = false;
|
|
bool _dSeatPrefFocus = false;
|
|
|
|
final FocusNode _iSeatPrefFocusNode = FocusNode();
|
|
final FocusNode _dSeatPrefFocusNode = FocusNode();
|
|
|
|
final List<String> headers = [
|
|
"Passport Details",
|
|
// "Local ID Details",
|
|
"Visa Details",
|
|
"Frequent Flyer Information",
|
|
"Hotel Loyalty Membership",
|
|
"Preferences Domestic",
|
|
"Preferences International",
|
|
"Other Details",
|
|
"Forex Details",
|
|
];
|
|
|
|
//To Create Controllers
|
|
List<String> dataHeader = [
|
|
"Fname",
|
|
"Mname",
|
|
"Lname",
|
|
"nationality",
|
|
"d_meal_pref",
|
|
"i_meal_pref",
|
|
"d_additonal_Info",
|
|
"i_additonal_Info",
|
|
"passportNumber",
|
|
"emergency_contact",
|
|
"placeOfIssue",
|
|
"passportDoc",
|
|
"dateOfIssue",
|
|
"dateOfExpiry",
|
|
"forex_card_num",
|
|
"full_name_as_id",
|
|
"local_id_num",
|
|
"forex_expiry_date",
|
|
];
|
|
|
|
// Color? layoutColor;
|
|
Color layoutColor = Colors.grey;
|
|
Color? bodyColor;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
// userId = getUserId();
|
|
|
|
apiCountryData = null;
|
|
apiHotelsData = [];
|
|
apiAirlineCountryData = [];
|
|
apiData = null;
|
|
for (var field in dataHeader) {
|
|
controllers[field] = TextEditingController();
|
|
focusNodes["${field}FocusNode"] = FocusNode();
|
|
focusStates["${field}Focused"] = false;
|
|
}
|
|
|
|
for (var key in focusNodes.keys) {
|
|
_addFocusListener(focusNodes[key]!, (focus) {
|
|
setState(() {
|
|
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
|
|
});
|
|
});
|
|
}
|
|
|
|
fetchCountries();
|
|
fetchHotels();
|
|
loadCountryList();
|
|
fetchApiData();
|
|
// Delay adding the row until after the first frame
|
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
|
// focusNodes["FnameFocusNode"]?.requestFocus();
|
|
setState(() {
|
|
addHotelLoyaltyEntry();
|
|
addFrequentFlierEntry();
|
|
addVisaEntry();
|
|
loadInitialData();
|
|
});
|
|
updateTravel();
|
|
print("Trying to focus Fname...");
|
|
|
|
final node = focusNodes["FnameFocusNode"];
|
|
if (node != null) {
|
|
print("Node exists, requesting focus");
|
|
node.requestFocus();
|
|
}
|
|
});
|
|
|
|
_addFocusListener(_iSeatPrefFocusNode, (focus) => _iSeatPrefFocus = focus);
|
|
_addFocusListener(_dSeatPrefFocusNode, (focus) => _dSeatPrefFocus = focus);
|
|
// _addFocusListener(_groupFocusNode, (focus) => _groupFocus = focus);
|
|
// _addFocusListener(_firstApprovalFocusNode, (focus) => _firstApprovalFocus = focus);
|
|
// _addFocusListener(_secondApprovalFocusNode, (focus) => _secondApprovalFocus = focus);
|
|
// _addFocusListener(_thirdApprovalFocusNode, (focus) => _thirdApprovalFocus = focus);
|
|
// _addFocusListener(_exceptionalApprovalFocusNode, (focus) => _exceptionalApprovalFocus = focus);
|
|
// _addFocusListener(_delegateFocusNode, (focus) => _delegateFocus = focus);
|
|
// _addFocusListener(_startDateFocusNode, (focus) => _startDateFocus = focus);
|
|
// _addFocusListener(_endDateFocusNode, (focus) => _endDateFocus = focus);
|
|
}
|
|
|
|
void _addFocusListener(FocusNode node, Function(bool) updateState) {
|
|
node.addListener(() {
|
|
setState(() {
|
|
updateState(node.hasFocus);
|
|
});
|
|
});
|
|
}
|
|
|
|
void loadInitialData() async {
|
|
String? layoutString = await getLayoutColor();
|
|
String? bodyStringColor = await getBodyColor();
|
|
|
|
setState(() {
|
|
layoutColor =
|
|
layoutString != null
|
|
? Color(int.parse(layoutString))
|
|
: Colors.redAccent;
|
|
|
|
bodyColor =
|
|
bodyStringColor != null
|
|
? Color(int.parse(bodyStringColor))
|
|
: Colors.white;
|
|
});
|
|
}
|
|
|
|
bool boolValidation() {
|
|
print("travel validation start");
|
|
print("visaEntries - $visaEntries");
|
|
|
|
final passportText = controllers["passportNumber"]?.text ?? "";
|
|
|
|
if (_selectedTripType == "Indian") {
|
|
// Match exactly 8 alphanumeric characters
|
|
final isValid = RegExp(r'^[a-zA-Z0-9]{8}$').hasMatch(passportText);
|
|
|
|
if (!isValid) {
|
|
setState(() {
|
|
errorMessages["passport_number"] = "Enter 8 characters";
|
|
});
|
|
return false;
|
|
} else {
|
|
setState(() {
|
|
errorMessages.remove("passport_number");
|
|
});
|
|
}
|
|
}
|
|
|
|
final format = DateFormat("dd-MM-yyyy");
|
|
|
|
DateTime? start_Date;
|
|
DateTime? end_Date;
|
|
|
|
try {
|
|
if (controllers["dateOfIssue"]?.text != null &&
|
|
controllers["dateOfIssue"]!.text.isNotEmpty) {
|
|
start_Date = format.parse(controllers["dateOfIssue"]!.text);
|
|
}
|
|
if (controllers["dateOfExpiry"]?.text != null &&
|
|
controllers["dateOfExpiry"]!.text.isNotEmpty) {
|
|
end_Date = format.parse(controllers["dateOfExpiry"]!.text);
|
|
}
|
|
|
|
if (start_Date != null &&
|
|
end_Date != null &&
|
|
end_Date.isBefore(start_Date)) {
|
|
setState(() {
|
|
errorMessages["date_of_expiry"] = "Invalid Date Range";
|
|
});
|
|
print("End date cannot be earlier than start date");
|
|
}
|
|
} catch (e) {
|
|
print("Invalid passport date format");
|
|
}
|
|
|
|
// Additional validation starts - travelDetailsData passport
|
|
// DateTime? start_Date = travelDetailsData?['date_of_issue'];
|
|
// DateTime? end_Date = travelDetailsData?['date_of_expiry'];
|
|
//
|
|
// if (start_Date != null &&
|
|
// end_Date != null &&
|
|
// start_Date.toString().isNotEmpty &&
|
|
// end_Date.toString().isNotEmpty) {
|
|
// try {
|
|
// final format = DateFormat("dd-MM-yyyy");
|
|
// final checkStartDate = format.parse("$start_Date");
|
|
// final checkEndDate = format.parse("$end_Date");
|
|
//
|
|
// if (checkEndDate.isBefore(checkStartDate)) {
|
|
// // return "End date cannot be earlier than start date";;
|
|
// return;
|
|
// }
|
|
// } catch (e) {
|
|
// // return "End date cannot be earlier than start date";
|
|
// return;
|
|
// // errorMessages["end_date"] = "Invalid date format";
|
|
// }
|
|
// }
|
|
|
|
// ✅ Visa entries: Validate valid_from < valid_upto for each entry
|
|
for (var entry in visaEntries) {
|
|
final localId = entry["local_id"];
|
|
final fromText = entry["controller_valid_from"]?.text.trim();
|
|
final toText = entry["controller_valid_upto"]?.text.trim();
|
|
|
|
if (fromText != null &&
|
|
toText != null &&
|
|
fromText.isNotEmpty &&
|
|
toText.isNotEmpty) {
|
|
try {
|
|
final fromDate = format.parseStrict(fromText);
|
|
final toDate = format.parseStrict(toText);
|
|
|
|
if (toDate.isBefore(fromDate)) {
|
|
print("VisaErroer1");
|
|
errorMessages["valid_upto${entry['local_id']}"] =
|
|
"Invalid date range";
|
|
// errorMessages["valid_upto_$localId"] =
|
|
// "Valid Upto can't be before Valid From";
|
|
} else {
|
|
print("VisaErroer2");
|
|
// errorMessages.remove("valid_upto$localId");
|
|
errorMessages.remove("valid_upto${entry['local_id']}");
|
|
}
|
|
} catch (e) {
|
|
print("VisaErroer3");
|
|
errorMessages["valid_upto${entry['local_id']}"] =
|
|
"Invalid date format";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (errorMessages.isNotEmpty) {
|
|
print("Validation failed with errors: $errorMessages");
|
|
setState(() {}); // Refresh UI to show errors
|
|
return false;
|
|
}
|
|
print("travel validation end");
|
|
return true;
|
|
}
|
|
|
|
Future<void> apiCheckDuplicate(
|
|
String label,
|
|
String field,
|
|
String value,
|
|
String? userId,
|
|
) async {
|
|
try {
|
|
final response = await apiService.CheckDuplicate(
|
|
context,
|
|
label,
|
|
field,
|
|
value,
|
|
userId,
|
|
);
|
|
|
|
if (response.isNotEmpty) {
|
|
_clearError(field);
|
|
errorMessages[field] = response['message'] ?? "$label Already Exists";
|
|
print("Duplicate found: ${response['message']}");
|
|
return;
|
|
} else {
|
|
_clearError(field);
|
|
print("No duplicates found.");
|
|
}
|
|
} catch (e) {
|
|
print("Error in checkDuplicate: $e");
|
|
}
|
|
}
|
|
|
|
// Future<void> apiCheckDuplicate(
|
|
// String label,
|
|
// String field,
|
|
// String value,
|
|
// String? userId,
|
|
// ) async {
|
|
// var newField = "";
|
|
// try {
|
|
// final response = await apiService.CheckDuplicate(
|
|
// label,
|
|
// newField,
|
|
// value,
|
|
// userId,
|
|
// );
|
|
// if (response.isNotEmpty) {
|
|
// _clearError(field);
|
|
// errorMessages[field] = response['message'] ?? "$label already exists";
|
|
// print("Duplicate found: ${response['message']}");
|
|
// return;
|
|
// } else {
|
|
// _clearError(field);
|
|
// print("No duplicates found.");
|
|
// }
|
|
// } catch (e) {
|
|
// print("Error in checkDuplicate: $e");
|
|
// }
|
|
// }
|
|
|
|
void _clearError(String field) {
|
|
setState(() {
|
|
errorMessages.remove(field);
|
|
});
|
|
}
|
|
|
|
//-------------------------Hotel Loyalty --------------------------------
|
|
void addHotelLoyaltyEntry() {
|
|
final localId = _rowhotelCounter++;
|
|
hotelLoyaltyEntries.add({
|
|
"local_id": localId,
|
|
"id": null, // to be set when backend responds
|
|
"hotel_name": null,
|
|
// "controller_hotel": TextEditingController(),
|
|
"controller_membership": TextEditingController(),
|
|
"is_active": "1",
|
|
});
|
|
setState(() {});
|
|
}
|
|
|
|
void removeHotelLoyaltyEntry(Map<String, dynamic> entry) {
|
|
print("removeHotelLoyaltyEntry- $entry");
|
|
|
|
setState(() {
|
|
entry["is_active"] = "0";
|
|
});
|
|
}
|
|
//-------------------------Frequent Flier--------------------------------
|
|
|
|
void addFrequentFlierEntry() {
|
|
final localId = _rowFlierCounter++;
|
|
frequentFlierEntries.add({
|
|
"local_id": localId,
|
|
"id": null, // to be set when backend responds
|
|
// "controller_airline": null,
|
|
"airline": null,
|
|
"controller_flier_number": TextEditingController(),
|
|
"is_active": "1",
|
|
});
|
|
setState(() {});
|
|
}
|
|
|
|
void removeFrequentFlierEntry(Map<String, dynamic> entry) {
|
|
print("removeFrequentFlierEntrydd- $entry");
|
|
|
|
setState(() {
|
|
entry["is_active"] = "0";
|
|
});
|
|
}
|
|
|
|
//-------------------------Visa --------------------------------
|
|
|
|
void addVisaEntry() {
|
|
final localId = _rowVisaCounter++;
|
|
visaEntries.add({
|
|
"local_id": localId,
|
|
"id": null, // to be set when backend responds
|
|
"country_code": null,
|
|
"visa_type_id": null,
|
|
"controller_valid_from": TextEditingController(),
|
|
"controller_valid_upto": TextEditingController(),
|
|
"is_active": "1",
|
|
});
|
|
setState(() {});
|
|
}
|
|
|
|
void removeVisaEntry(Map<String, dynamic> entry) {
|
|
print("removeFrequentFlierEntrydd- $entry");
|
|
|
|
setState(() {
|
|
entry["is_active"] = "0";
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
for (var entry in hotelLoyaltyEntries) {
|
|
// entry["controller_hotel"].dispose();
|
|
entry["controller_membership"].dispose();
|
|
}
|
|
|
|
for (var entry in frequentFlierEntries) {
|
|
// entry["controller_airline"].dispose();
|
|
entry["controller_flier_number"].dispose();
|
|
}
|
|
|
|
for (var entry in visaEntries) {
|
|
entry["controller_valid_from"].dispose();
|
|
entry["controller_valid_upto"].dispose();
|
|
}
|
|
super.dispose();
|
|
}
|
|
|
|
void disposeController(dynamic controller) {
|
|
if (controller is TextEditingController) {
|
|
controller.dispose();
|
|
}
|
|
}
|
|
|
|
Map<String, dynamic> get travel_Detials {
|
|
Map<String, dynamic> data = {
|
|
"passport_firstname": controllers["Fname"]?.text,
|
|
"passport_middlename": controllers["Mname"]?.text,
|
|
"passport_lastname": controllers["Lname"]?.text,
|
|
"nationality": _selectedTripType,
|
|
"passport_number": controllers["passportNumber"]?.text ?? '',
|
|
"place_of_issue": controllers["placeOfIssue"]?.text,
|
|
"date_of_issue": controllers["dateOfIssue"]?.text,
|
|
"date_of_expiry": controllers["dateOfExpiry"]?.text,
|
|
"d_meal_preference": controllers["d_meal_pref"]?.text,
|
|
"d_seat_preference": selectedDomesticSeat,
|
|
"d_additonalInfo": controllers["d_additonal_Info"]?.text,
|
|
"i_meal_preference": controllers["i_meal_pref"]?.text,
|
|
"i_seat_preference": selectedIntenationalSeat,
|
|
"i_additonalInfo": controllers["i_additonal_Info"]?.text,
|
|
"emergency_contact_number": controllers["emergency_contact"]?.text,
|
|
"forex_pre_paid_card_number": controllers["forex_card_num"]?.text,
|
|
"forex_expiry_date": controllers["forex_expiry_date"]?.text,
|
|
// "frequent_flier_information": [],
|
|
"frequent_flier_information": frequentFlierList,
|
|
"hotel_membership": hotelMembershipList,
|
|
"visa_details": visaDetailsList,
|
|
// "local_id_type": selectedLocalIdType,
|
|
// "local_id_num": controllers["local_id_num"]?.text,
|
|
// "full_name_as_id": controllers["full_name_as_id"]?.text,
|
|
};
|
|
return data;
|
|
}
|
|
|
|
// List<Map<String, dynamic>> get hotelMembershipList {
|
|
// final mappedList = hotelLoyaltyEntries.map((entry) {
|
|
// return {
|
|
// "id": entry["id"],
|
|
// "hotel_name": entry["controller_hotel"].text ?? '',
|
|
// "membership_number": entry["controller_membership"].text ?? '',
|
|
// "created_by": null,
|
|
// "updated_by": null,
|
|
// "is_active": entry["is_active"] ?? "1",
|
|
// };
|
|
// }).toList();
|
|
//
|
|
// final allEntriesEmpty = mappedList.every((entry) =>
|
|
// (entry["hotel_name"] as String).trim().isEmpty &&
|
|
// (entry["membership_number"] as String).trim().isEmpty);
|
|
//
|
|
// return allEntriesEmpty ? [] : mappedList;
|
|
// }
|
|
|
|
List<Map<String, dynamic>> get hotelMembershipList {
|
|
final mappedList =
|
|
hotelLoyaltyEntries.map((entry) {
|
|
final membership_number = entry["controller_membership"];
|
|
// final hotelName = entry["controller_hotel"];
|
|
|
|
print("membership_number- ${membership_number}");
|
|
|
|
return {
|
|
"id": entry["id"],
|
|
"hotel_id": entry["hotel_id"] ?? "",
|
|
// "hotel_name": entry["hotel_id"] ?? "",
|
|
"hotel_name": entry["hotel_name"] ?? "",
|
|
// "hotel_name": hotelName is TextEditingController ? hotelName.text : "",
|
|
"membership_number":
|
|
membership_number is TextEditingController
|
|
? membership_number.text
|
|
: "",
|
|
"created_by": null,
|
|
"updated_by": null,
|
|
"is_active": entry["is_active"] ?? "1",
|
|
};
|
|
}).toList();
|
|
|
|
final allEntriesEmpty = mappedList.every(
|
|
(entry) =>
|
|
(entry["hotel_id"] as String).trim().isEmpty &&
|
|
(entry["membership_number"] as String).trim().isEmpty,
|
|
);
|
|
|
|
return allEntriesEmpty ? [] : mappedList;
|
|
}
|
|
|
|
// List<Map<String, dynamic>> get frequentFlierList {
|
|
// return frequentFlierEntries
|
|
// // .where((entry) => entry["is_active"] != "0") // ✅ Only active entries
|
|
// .map((entry) {
|
|
// return {
|
|
// "id": entry["id"],
|
|
// "airline": entry["airline"],
|
|
// "frequent_flier_number": entry["controller_flier_number"].text,
|
|
// "created_by": null,
|
|
// "updated_by": null,
|
|
// "is_active": entry["is_active"] ?? "1",
|
|
// };
|
|
// }).toList();
|
|
// }
|
|
|
|
List<Map<String, dynamic>> get frequentFlierList {
|
|
final mappedList =
|
|
frequentFlierEntries.map((entry) {
|
|
final frequent_flier_number = entry["controller_flier_number"];
|
|
|
|
print("frequent_flier_number- $frequent_flier_number");
|
|
|
|
return {
|
|
"id": entry["id"],
|
|
"airline": entry["airline"] ?? "",
|
|
"frequent_flier_number":
|
|
frequent_flier_number is TextEditingController
|
|
? frequent_flier_number.text
|
|
: "",
|
|
"created_by": null,
|
|
"updated_by": null,
|
|
"is_active": entry["is_active"] ?? "1",
|
|
};
|
|
}).toList();
|
|
|
|
final allEntriesEmpty = mappedList.every(
|
|
(entry) =>
|
|
(entry["airline"] as String).trim().isEmpty &&
|
|
(entry["frequent_flier_number"] as String).trim().isEmpty,
|
|
);
|
|
|
|
return allEntriesEmpty ? [] : mappedList;
|
|
}
|
|
|
|
// List<Map<String, dynamic>> get visaDetailsList {
|
|
// return visaEntries
|
|
// // .where((entry) => entry["is_active"] != "0") // ✅ Only active entries
|
|
// .map((entry) {
|
|
// return {
|
|
// "id": entry["id"],
|
|
// "country_code": entry["country_code"],
|
|
// "visa_type_id": entry["visa_type_id"],
|
|
// "valid_from": entry["controller_valid_from"].text,
|
|
// "valid_upto": entry["controller_valid_upto"].text,
|
|
// // "valid_from": entry["controller_valid_from"] != null
|
|
// // ? entry["controller_valid_from"].text
|
|
// // : "",
|
|
// // "valid_upto": entry["controller_valid_upto"] != null
|
|
// // ? entry["controller_valid_upto"].text
|
|
// // : "",
|
|
// "created_by": null,
|
|
// "updated_by": null,
|
|
// "is_active": entry["is_active"] ?? "1",
|
|
// };
|
|
// }).toList();
|
|
// }
|
|
|
|
List<Map<String, dynamic>> get visaDetailsList {
|
|
final mappedList =
|
|
visaEntries.map((entry) {
|
|
final fromController = entry["controller_valid_from"];
|
|
final uptoController = entry["controller_valid_upto"];
|
|
|
|
return {
|
|
"id": entry["id"],
|
|
"country_code": entry["country_code"], // ✅ string value
|
|
"visa_type_id": entry["visa_type_id"], // ✅ string value
|
|
"valid_from":
|
|
fromController is TextEditingController
|
|
? fromController.text
|
|
: "",
|
|
"valid_upto":
|
|
uptoController is TextEditingController
|
|
? uptoController.text
|
|
: "",
|
|
"created_by": null,
|
|
"updated_by": null,
|
|
"is_active": entry["is_active"] ?? "1",
|
|
};
|
|
}).toList();
|
|
|
|
final allEntriesEmpty = mappedList.every(
|
|
(entry) =>
|
|
(entry["country_code"] == null ||
|
|
entry["country_code"].toString().trim().isEmpty) &&
|
|
(entry["visa_type_id"] == null ||
|
|
entry["visa_type_id"].toString().trim().isEmpty) &&
|
|
(entry["valid_from"] == null ||
|
|
entry["valid_from"].toString().trim().isEmpty) &&
|
|
(entry["valid_upto"] == null ||
|
|
entry["valid_upto"].toString().trim().isEmpty),
|
|
);
|
|
|
|
return allEntriesEmpty ? [] : mappedList;
|
|
}
|
|
|
|
void updateTravel() {
|
|
print("widget.travelDetails");
|
|
print(widget.travelDetails);
|
|
if (widget.travelDetails != null) {
|
|
print("Updatewidget.travelDetails");
|
|
print(widget.travelDetails);
|
|
|
|
controllers["Fname"]?.text =
|
|
widget.travelDetails?["passport_firstname"] ?? "";
|
|
|
|
controllers["Mname"]?.text =
|
|
widget.travelDetails?["passport_middlename"] ?? "";
|
|
|
|
controllers["Lname"]?.text =
|
|
widget.travelDetails?["passport_lastname"] ?? "";
|
|
|
|
_selectedTripType = widget.travelDetails?["nationality"] ?? "";
|
|
|
|
controllers["passportNumber"]?.text =
|
|
widget.travelDetails?["passport_number"] ?? "";
|
|
controllers["placeOfIssue"]?.text =
|
|
widget.travelDetails?["place_of_issue"] ?? "";
|
|
controllers["dateOfIssue"]?.text =
|
|
widget.travelDetails?["date_of_issue"] ?? "";
|
|
controllers["dateOfExpiry"]?.text =
|
|
widget.travelDetails?["date_of_expiry"] ?? "";
|
|
|
|
controllers["d_meal_pref"]?.text =
|
|
widget.travelDetails?["d_meal_preference"] ?? "";
|
|
controllers["i_meal_pref"]?.text =
|
|
widget.travelDetails?["i_meal_preference"] ?? "";
|
|
|
|
controllers["d_additonal_Info"]?.text =
|
|
widget.travelDetails?["d_additonalInfo"] ?? "";
|
|
controllers["i_additonal_Info"]?.text =
|
|
widget.travelDetails?["i_additonalInfo"] ?? "";
|
|
|
|
selectedIntenationalSeat =
|
|
widget.travelDetails?["i_seat_preference"] ?? "";
|
|
selectedDomesticSeat = widget.travelDetails?["d_seat_preference"] ?? "";
|
|
|
|
controllers["emergency_contact"]?.text =
|
|
widget.travelDetails?["emergency_contact_number"] ?? "";
|
|
|
|
controllers["forex_card_num"]?.text =
|
|
widget.travelDetails?["forex_pre_paid_card_number"] ?? "";
|
|
controllers["forex_expiry_date"]?.text =
|
|
widget.travelDetails?["forex_expiry_date"] ?? "";
|
|
|
|
String? apiDocPath = widget.travelDetails?["passport_document"];
|
|
if (apiDocPath != null && apiDocPath.isNotEmpty) {
|
|
passportFileUrlFromApi = apiDocPath;
|
|
selectedFileNames = apiDocPath.split('/').last;
|
|
passportFile = null;
|
|
} else {
|
|
passportFileUrlFromApi = null;
|
|
selectedFileNames = null;
|
|
passportFile = null;
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
|
|
// 🚨 Hotel Membership Logic (pre-fill for edit)
|
|
// for (var entry in hotelLoyaltyEntries) {
|
|
// // entry["controller_hotel"].dispose();
|
|
// entry["controller_membership"].dispose();
|
|
// }
|
|
// hotelLoyaltyEntries.clear();
|
|
|
|
for (var entry in hotelLoyaltyEntries) {
|
|
disposeController(entry["controller_membership"]);
|
|
// (entry["controller_membership"] as TextEditingController?)?.dispose();
|
|
}
|
|
|
|
if (!mounted) return; // ✅ avoids context errors
|
|
print(MediaQuery.of(context).size);
|
|
//
|
|
// for (var entry in hotelLoyaltyEntries) {
|
|
// (entry["controller_membership"] as TextEditingController?)?.dispose();
|
|
// }
|
|
hotelLoyaltyEntries.clear();
|
|
|
|
final hotelMembershipList = widget.travelDetails?['hotel_membership'];
|
|
if (hotelMembershipList != null && hotelMembershipList is List) {
|
|
for (var item in hotelMembershipList) {
|
|
if (item["is_active"]?.toString() == "0") continue;
|
|
|
|
final localId = _rowhotelCounter++;
|
|
final hotelDataId = item["hotel_id"];
|
|
hotelLoyaltyEntries.add({
|
|
"local_id": localId,
|
|
"id": item["id"],
|
|
// "controller_hotel":
|
|
// TextEditingController(text: item["hotel_name"] ?? ""),
|
|
"hotel_name": item["hotel_name"] ?? "",
|
|
"hotel_id": hotelDataId ?? "",
|
|
// "controller_membership":
|
|
// TextEditingController(text: item["membership_number"] ?? ""),
|
|
"controller_membership": TextEditingController(
|
|
text: item["membership_number"] ?? "",
|
|
),
|
|
});
|
|
}
|
|
|
|
if (hotelLoyaltyEntries.isEmpty) {
|
|
addHotelLoyaltyEntry();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
// 🚨 Visa Logic (pre-fill for edit)
|
|
for (var entry in visaEntries) {
|
|
entry["controller_valid_from"]?.dispose();
|
|
entry["controller_valid_upto"]?.dispose();
|
|
}
|
|
visaEntries.clear();
|
|
|
|
final visaDetailsList = widget.travelDetails?['visa_details'];
|
|
if (visaDetailsList != null && visaDetailsList is List) {
|
|
for (var item in visaDetailsList) {
|
|
if (item["is_active"]?.toString() == "0") continue;
|
|
|
|
final localId = _rowVisaCounter++;
|
|
visaEntries.add({
|
|
"local_id": localId,
|
|
"id": item["id"],
|
|
"country_code": item["country_code"],
|
|
"visa_type_id": item["visa_type_id"],
|
|
"controller_valid_from": TextEditingController(
|
|
text: item["valid_from"] ?? "",
|
|
),
|
|
"controller_valid_upto": TextEditingController(
|
|
text: item["valid_upto"] ?? "",
|
|
),
|
|
});
|
|
}
|
|
|
|
if (visaEntries.isEmpty) {
|
|
addVisaEntry();
|
|
}
|
|
}
|
|
// ----------------------------------------------------
|
|
|
|
// 🚨 Frequent Flier Logic (pre-fill for edit)
|
|
// for (var entry in frequentFlierEntries) {
|
|
// // entry["controller_airline"].dispose();
|
|
// // entry["controller_flier_number"].dispose();
|
|
// (entry["controller_flier_number"] as TextEditingController?)?.dispose();
|
|
// }
|
|
for (var entry in frequentFlierEntries) {
|
|
entry["controller_flier_number"]?.dispose();
|
|
}
|
|
|
|
frequentFlierEntries.clear();
|
|
|
|
print(MediaQuery.of(context).size);
|
|
|
|
final frequentFlierList =
|
|
widget.travelDetails?['frequent_flier_information'];
|
|
|
|
if (frequentFlierList != null && frequentFlierList is List) {
|
|
for (var item in frequentFlierList) {
|
|
if (item["is_active"]?.toString() == "0") continue;
|
|
|
|
final localId = _rowFlierCounter++;
|
|
final airlineCode = item["airline"];
|
|
|
|
frequentFlierEntries.add({
|
|
"local_id": localId,
|
|
"id": item["id"],
|
|
"airline": airlineCode ?? "",
|
|
"controller_flier_number": TextEditingController(
|
|
text: item["frequent_flier_number"] ?? "",
|
|
),
|
|
});
|
|
}
|
|
|
|
if (frequentFlierEntries.isEmpty) {
|
|
addFrequentFlierEntry();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
|
|
setState(() {});
|
|
}
|
|
}
|
|
|
|
Future<void> fetchCountries() async {
|
|
try {
|
|
List<dynamic> countries = await apiService.fetchCountryList(context);
|
|
setState(() {
|
|
apiCountryData = countries;
|
|
});
|
|
} catch (e) {
|
|
print('Error fetching country list: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> fetchHotels() async {
|
|
try {
|
|
List<dynamic> countries = await apiService.fetchHotelsList(context);
|
|
setState(() {
|
|
apiHotelsData = countries;
|
|
});
|
|
} catch (e) {
|
|
print('Error fetching country list: $e');
|
|
}
|
|
}
|
|
|
|
Future<void> loadCountryList() async {
|
|
final newTripType = "2";
|
|
final result = await apiService.fetchAirlineList(context);
|
|
// final result = await apiService.fetchFlightsCountryList(newTripType);
|
|
|
|
print("resultFlight: $result ");
|
|
|
|
setState(() {
|
|
apiAirlineCountryData = result;
|
|
});
|
|
|
|
// print("ResultCountry : $result");
|
|
//
|
|
// // Create a map: Country_Code -> "City, Airport"
|
|
// Map<String, String> tempCountryMap = {};
|
|
//
|
|
// for (var country in result) {
|
|
// String city = country['City'] ?? '';
|
|
// String airport = country['Airport'] ?? '';
|
|
// String displayName = '${country['City']} - ${country['Airport']}';
|
|
//
|
|
// tempCountryMap[country['Code']] = displayName;
|
|
// }
|
|
//
|
|
// setState(() {
|
|
// countryMap = tempCountryMap; // Update the map
|
|
// isCountryLoading = false;
|
|
// });
|
|
}
|
|
|
|
Future<void> fetchApiData() async {
|
|
try {
|
|
final data = await apiService.fetchMasterDropdown(context);
|
|
|
|
print("fetchApiData - $data");
|
|
|
|
if (data is! Map) {
|
|
throw Exception("Invalid response format: expected a Map");
|
|
}
|
|
if (!mounted) return;
|
|
setState(() {
|
|
apiData = data;
|
|
});
|
|
} catch (e) {
|
|
print('Error fetching role list: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Container(
|
|
height: MediaQuery.of(context).size.height,
|
|
padding: const EdgeInsets.only(top: 8),
|
|
decoration: BoxDecoration(
|
|
border: Border(
|
|
top: BorderSide(
|
|
color: Colors.blueGrey.shade300, // Set your desired color
|
|
width: 0.09, // Set border thickness
|
|
),
|
|
),
|
|
),
|
|
child: SingleChildScrollView(
|
|
scrollDirection: Axis.vertical,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
...List.generate(headers.length, (index) {
|
|
final isExpanded = expandedIndex == index;
|
|
|
|
return Card(
|
|
color: isExpanded ? Color(0xFFFFFDF3) : Colors.white,
|
|
elevation: isExpanded ? 0.9 : 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
margin: const EdgeInsets.symmetric(vertical: 5),
|
|
child: InkWell(
|
|
focusColor: Color(0xFFFFFDF3),
|
|
hoverColor: Color(0xFFFFFDF3),
|
|
highlightColor: Color(0xFFFFFDF3), // removes grey highlight
|
|
splashColor: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
onTap: () {
|
|
setState(() {
|
|
expandedIndex = isExpanded ? null : index;
|
|
});
|
|
},
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 8,
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
headers[index],
|
|
style: GoogleFonts.poppins(
|
|
fontSize: widget.isDesktop ? 12 : 11,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.black87,
|
|
),
|
|
),
|
|
Icon(
|
|
isExpanded
|
|
? Icons.keyboard_arrow_up
|
|
: Icons.keyboard_arrow_down,
|
|
color:
|
|
isExpanded
|
|
? Colors.black87
|
|
: Colors.grey[600],
|
|
),
|
|
],
|
|
),
|
|
AnimatedSize(
|
|
duration: const Duration(milliseconds: 250),
|
|
curve: Curves.easeInOut,
|
|
child: ConstrainedBox(
|
|
constraints:
|
|
isExpanded
|
|
? const BoxConstraints()
|
|
: const BoxConstraints(maxHeight: 0),
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(top: 8),
|
|
child: Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: _buildExpandedContent(index),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildExpandedContent(int index) {
|
|
switch (headers[index]) {
|
|
case "Passport Details":
|
|
return _buildPassportDatas(widget.isDesktop);
|
|
|
|
// case "Local ID Details":
|
|
// return _buildLocalIdDetails(widget.isDesktop);
|
|
|
|
case "Visa Details":
|
|
return _buildVisaDetails(widget.isDesktop);
|
|
|
|
case "Frequent Flyer Information":
|
|
return _buildFrequentFlierInfo(widget.isDesktop);
|
|
|
|
case "Hotel Loyalty Membership":
|
|
return _buildHotelLoyalty(widget.isDesktop);
|
|
|
|
case "Preferences Domestic":
|
|
return _buildPreferencesDomestic(widget.isDesktop);
|
|
//
|
|
case "Preferences International":
|
|
return _buildPreferencesInternational(widget.isDesktop);
|
|
|
|
case "Other Details":
|
|
return _buildOtherDetails(widget.isDesktop);
|
|
|
|
case "Forex Details":
|
|
return _buildForexDetails(widget.isDesktop);
|
|
|
|
default:
|
|
return Text(
|
|
"Form fields for ${headers[index]} go here",
|
|
style: GoogleFonts.poppins(fontSize: 12),
|
|
);
|
|
}
|
|
}
|
|
|
|
//------------------------------------Passport Details-----------------------------------
|
|
|
|
Widget _buildPassportDatas(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// SizedBox(
|
|
// height: 10,
|
|
// ),
|
|
_buildPassportDataRow1(widget.isDesktop),
|
|
SizedBox(height: 10),
|
|
_buildPassportDataRow2(widget.isDesktop),
|
|
SizedBox(height: 10),
|
|
_buildPassportDataRow3(widget.isDesktop),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildPassportDataRow1(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
// child: Row(
|
|
// children: [
|
|
// Text(
|
|
// "Name as per passport : ",
|
|
// style: GoogleFonts.poppins(
|
|
// fontSize: 11,
|
|
// fontStyle: FontStyle.italic,
|
|
// letterSpacing: 0.5,
|
|
// ),
|
|
// ),
|
|
// Text(
|
|
// "${widget.fullName} ",
|
|
// style: GoogleFonts.poppins(
|
|
// fontSize: 10,
|
|
// fontWeight: FontWeight.w600,
|
|
// letterSpacing: 0.5,
|
|
// color: Colors.black87,
|
|
// // fontStyle: FontStyle.italic,
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// ),
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildFirstNameField(),
|
|
Spacer(), buildMiddleNameField(),
|
|
Spacer(),
|
|
buildLastNameField(),
|
|
SizedBox(width: 25), // Space after Last Name
|
|
buildNationality(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildFirstNameField(),
|
|
SizedBox(height: 8), buildMiddleNameField(),
|
|
SizedBox(height: 8), // Vertical space
|
|
buildLastNameField(),
|
|
SizedBox(height: 8),
|
|
buildNationality(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildPassportDataRow2(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildPassportNumber(),
|
|
Spacer(),
|
|
buildPlaceOfIssue(),
|
|
Spacer(),
|
|
|
|
/// Space after Last Name
|
|
buildDateOfIssue(),
|
|
|
|
SizedBox(width: 25),
|
|
buildDateOfExpiry(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildPassportNumber(),
|
|
SizedBox(height: 8), // Vertical space
|
|
buildPlaceOfIssue(),
|
|
SizedBox(height: 8),
|
|
buildDateOfIssue(),
|
|
SizedBox(height: 8),
|
|
buildDateOfExpiry(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget _buildPassportDataRow3(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [buildPassportDocument()],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [buildPassportDocument()],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildFirstNameField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"First Name",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
// isFocused: false,
|
|
isFocused: focusStates["FnameFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["FnameFocusNode"],
|
|
controller: controllers["Fname"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("first_name");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "FirstName",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildMiddleNameField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Middle Name",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: focusStates["MnameFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["MnameFocusNode"],
|
|
controller: controllers["Mname"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("middle_name");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "MiddleName",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildLastNameField() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Last Name",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: focusStates["LnameFocused"] ?? false,
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.13
|
|
: null,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["LnameFocusNode"],
|
|
controller: controllers["Lname"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("last_name");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "LastName",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildNationality() {
|
|
List<String> nationalityOptions = [
|
|
"Indian",
|
|
"International",
|
|
"other nationality",
|
|
];
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Nationality",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
// isFocused: false,
|
|
isFocused: focusStates["nationality"] ?? false,
|
|
padding: const EdgeInsets.symmetric(horizontal: 0),
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.13
|
|
: null,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: Focus(
|
|
focusNode: focusNodes["nationalityFocusNode"],
|
|
onFocusChange: (hasFocus) {
|
|
setState(() {
|
|
focusStates["nationalityFocused"] = hasFocus;
|
|
});
|
|
},
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
// Request focus when user taps
|
|
focusNodes["nationalityFocusNode"]?.requestFocus();
|
|
},
|
|
child: DropdownSearch<String>(
|
|
selectedItem: _selectedTripType,
|
|
enabled: !widget.isViewMode,
|
|
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: false, // Set true if you want search
|
|
fit: FlexFit.loose,
|
|
constraints: BoxConstraints(maxHeight: 200),
|
|
itemBuilder:
|
|
(context, item, isSelected) => Container(
|
|
color: Colors.white,
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 6,
|
|
),
|
|
child: Text(
|
|
item,
|
|
style: GoogleFonts.poppins(fontSize: 11.5),
|
|
),
|
|
),
|
|
),
|
|
items: nationalityOptions,
|
|
dropdownDecoratorProps: DropDownDecoratorProps(
|
|
dropdownSearchDecoration: InputDecoration(
|
|
// border: InputBorder.none,
|
|
// contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(1),
|
|
borderSide: BorderSide(
|
|
color:
|
|
(focusStates["nationalityFocused"] ?? false)
|
|
? (layoutColor)
|
|
: Colors.white,
|
|
width: 1,
|
|
),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color:
|
|
(focusStates["nationalityFocused"] ?? false)
|
|
? (layoutColor)
|
|
: Colors.white,
|
|
// : const Color(0xFFD6D5E6),
|
|
width: 1,
|
|
// const Color(0xFFD6D5E6),
|
|
),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: (layoutColor),
|
|
width: 0.5,
|
|
),
|
|
),
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 8,
|
|
vertical: 1,
|
|
),
|
|
),
|
|
),
|
|
dropdownBuilder:
|
|
(context, selectedItem) => Align(
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedItem ?? "Select",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
onChanged:
|
|
widget.isViewMode
|
|
? null
|
|
: (String? newValue) {
|
|
setState(() {
|
|
_selectedTripType = newValue;
|
|
controllers["passportNumber"]?.clear();
|
|
print("_selectedTripType - $_selectedTripType");
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildPassportNumber() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Passport Number",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: focusStates["passportNumberFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["passportNumberFocusNode"],
|
|
controller: controllers["passportNumber"],
|
|
enabled: !widget.isViewMode,
|
|
inputFormatters: [
|
|
// limit to 8 characters
|
|
FilteringTextInputFormatter.allow(
|
|
RegExp(r'[a-zA-Z0-9]'),
|
|
), // allow only letters and digits
|
|
if (_selectedTripType == "Indian")
|
|
LengthLimitingTextInputFormatter(8),
|
|
],
|
|
onChanged: (value) {
|
|
_clearError("passport_number");
|
|
apiCheckDuplicate(
|
|
"Passport Number",
|
|
"passport_number",
|
|
value,
|
|
widget.userIdApi,
|
|
);
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Passport Number",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (errorMessages["passport_number"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
errorMessages["passport_number"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildPlaceOfIssue() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Place of Issue",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: focusStates["placeOfIssueFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["placeOfIssueFocusNode"],
|
|
controller: controllers["placeOfIssue"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("placeOfIssue");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Place of Issue",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
DateTime? _selectedDateOfIssue;
|
|
DateTime? _selectedDateOfExpiry;
|
|
|
|
Widget buildDateOfIssue() {
|
|
Future<void> _selectCheckDateOfIssue(BuildContext context) async {
|
|
DateTime now = DateTime.now();
|
|
DateTime today = DateTime(now.year, now.month, now.day);
|
|
|
|
DateTime? pickedDate = await showDatePicker(
|
|
context: context,
|
|
initialDate:
|
|
_selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today)
|
|
? _selectedDateOfIssue!
|
|
: today,
|
|
firstDate: DateTime(1900),
|
|
// lastDate: DateTime(2100),
|
|
lastDate: today,
|
|
initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
);
|
|
|
|
if (pickedDate != null && pickedDate != _selectedDateOfIssue) {
|
|
setState(() {
|
|
_selectedDateOfIssue = pickedDate;
|
|
controllers["dateOfIssue"]?.text = DateFormat(
|
|
'dd-MM-yyyy',
|
|
).format(pickedDate);
|
|
|
|
// Revalidate expiry
|
|
if (_selectedDateOfExpiry != null &&
|
|
_selectedDateOfExpiry!.isBefore(_selectedDateOfIssue!)) {
|
|
errorMessages["date_of_expiry"] = "Invalid Date Range";
|
|
} else {
|
|
errorMessages.remove("date_of_expiry");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Date of Issue",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.13
|
|
: null,
|
|
isFocused: focusStates["dateOfIssueFocused"] ?? false,
|
|
// isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: GestureDetector(
|
|
onTap:
|
|
widget.isViewMode
|
|
? null
|
|
: () async {
|
|
await _selectCheckDateOfIssue(context);
|
|
if (controllers["dateOfIssue"]!.text.isNotEmpty) {
|
|
setState(() {
|
|
// errorMessages.remove("start_date");
|
|
});
|
|
}
|
|
focusNodes["dateOfIssueFocusNode"]?.requestFocus();
|
|
},
|
|
child: AbsorbPointer(
|
|
child: TextField(
|
|
controller: controllers["dateOfIssue"],
|
|
focusNode: focusNodes["dateOfIssueFocusNode"],
|
|
readOnly: true,
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
decoration: InputDecoration(
|
|
labelText: "Select Date",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
suffixIcon: const Icon(
|
|
Icons.calendar_today,
|
|
size: 16,
|
|
color: Color(0xFF8B8FB2),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildDateOfExpiry() {
|
|
Future<void> _selectCheckDateOfExpiry(BuildContext context) async {
|
|
DateTime now = DateTime.now();
|
|
DateTime today = DateTime(now.year, now.month, now.day);
|
|
// DateTime firstDate = today;
|
|
DateTime? validFromDate;
|
|
|
|
try {
|
|
String? fromDateText = controllers["dateOfIssue"]?.text.trim();
|
|
print("dateOfIssue From Text: $fromDateText");
|
|
if (fromDateText != null && fromDateText.isNotEmpty) {
|
|
validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText);
|
|
print("dateOfIssue Valid From: $validFromDate");
|
|
}
|
|
} catch (e) {
|
|
print("Error parsing valid from date: $e");
|
|
}
|
|
|
|
// Use max(today, validFromDate) as firstDate
|
|
// DateTime firstDate = today;
|
|
DateTime firstDate = validFromDate ?? today;
|
|
|
|
// Parse dateOfIssue safely
|
|
// DateTime firstDate;
|
|
// try {
|
|
// firstDate = DateFormat(
|
|
// "yyyy-MM-dd",
|
|
// ).parse(controllers["dateOfIssue"]!.text);
|
|
// } catch (e) {
|
|
// firstDate = today; // fallback if parsing fails
|
|
// }
|
|
|
|
DateTime? pickedDate = await showDatePicker(
|
|
context: context,
|
|
|
|
// initialDate:
|
|
// _selectedDateOfExpiry != null &&
|
|
// _selectedDateOfExpiry!.isAfter(firstDate)
|
|
// ? _selectedDateOfExpiry!
|
|
// : today,
|
|
initialDate: firstDate,
|
|
firstDate: firstDate,
|
|
// firstDate: DateTime(1900),
|
|
lastDate: DateTime(2100),
|
|
initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
);
|
|
|
|
if (pickedDate != null) {
|
|
setState(() {
|
|
_selectedDateOfExpiry = pickedDate;
|
|
controllers["dateOfExpiry"]?.text = DateFormat(
|
|
"dd-MM-yyyy",
|
|
).format(pickedDate);
|
|
|
|
if (_selectedDateOfIssue != null &&
|
|
_selectedDateOfExpiry!.isBefore(_selectedDateOfIssue!)) {
|
|
errorMessages["date_of_expiry"] = "Invalid Date Range";
|
|
} else {
|
|
errorMessages.remove("date_of_expiry");
|
|
}
|
|
});
|
|
}
|
|
|
|
// if (pickedDate != null && pickedDate != _selectedDateOfExpiry) {
|
|
// setState(() {
|
|
// _selectedDateOfExpiry = pickedDate;
|
|
// controllers["dateOfExpiry"]?.text = DateFormat(
|
|
// 'dd-MM-yyyy',
|
|
// ).format(pickedDate);
|
|
//
|
|
// // controllers["dateOfExpiry"]?.text =
|
|
// // DateFormat('dd-MM-yyyy').format(pickedDate);
|
|
// });
|
|
// }
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Date of Expiry",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
// isFocused: false,
|
|
isFocused: focusStates["dateOfExpiryFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.13
|
|
: null,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: GestureDetector(
|
|
onTap:
|
|
widget.isViewMode
|
|
? null
|
|
: () async {
|
|
await _selectCheckDateOfExpiry(context);
|
|
if (controllers["dateOfExpiry"]!.text.isNotEmpty) {
|
|
setState(() {
|
|
// errorMessages.remove("start_date");
|
|
});
|
|
}
|
|
focusNodes["dateOfExpiryFocusNode"]?.requestFocus();
|
|
},
|
|
child: AbsorbPointer(
|
|
child: TextField(
|
|
controller: controllers["dateOfExpiry"],
|
|
focusNode: focusNodes["dateOfExpiryFocusNode"],
|
|
readOnly: true,
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
decoration: InputDecoration(
|
|
labelText: "Select Date",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
suffixIcon: const Icon(
|
|
Icons.calendar_today,
|
|
size: 16,
|
|
color: Color(0xFF8B8FB2),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (errorMessages["date_of_expiry"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
errorMessages["date_of_expiry"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 10),
|
|
maxLines: 2, // Allow it to wrap onto two lines
|
|
overflow:
|
|
TextOverflow.ellipsis, // Add ellipsis if it still overflows
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
// Widget buildDateOfIssue() {
|
|
// DateTime? _selectedDateOfIssue;
|
|
//
|
|
// Future<void> _selectCheckDateOfIssue(BuildContext context) async {
|
|
// DateTime now = DateTime.now();
|
|
// DateTime today = DateTime(now.year, now.month, now.day);
|
|
//
|
|
// DateTime? pickedDate = await showDatePicker(
|
|
// context: context,
|
|
// initialDate:
|
|
// _selectedDateOfIssue != null && _selectedDateOfIssue!.isAfter(today)
|
|
// ? _selectedDateOfIssue!
|
|
// : today,
|
|
// // firstDate: today,
|
|
// firstDate: DateTime(1900),
|
|
// lastDate: DateTime(2100),
|
|
// initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
// );
|
|
//
|
|
// if (pickedDate != null && pickedDate != _selectedDateOfIssue) {
|
|
// setState(() {
|
|
// _selectedDateOfIssue = pickedDate;
|
|
// controllers["dateOfIssue"]?.text = DateFormat(
|
|
// 'dd-MM-yyyy',
|
|
// ).format(pickedDate);
|
|
// });
|
|
// }
|
|
// }
|
|
//
|
|
// return Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// Text(
|
|
// "Date of Issue",
|
|
// style: GoogleFonts.poppins(
|
|
// fontSize: 12,
|
|
// fontWeight: FontWeight.w600,
|
|
// color: Color(0xFF575A74),
|
|
// ),
|
|
// ),
|
|
// SizedBox(height: 5),
|
|
// CustomTextFieldUserTravellerWrapper(
|
|
// width:
|
|
// widget.isDesktop
|
|
// ? MediaQuery.of(context).size.width * 0.13
|
|
// : null,
|
|
// isFocused: false,
|
|
// isDesktop: widget.isDesktop,
|
|
// child: SizedBox(
|
|
// height: 40,
|
|
// child: GestureDetector(
|
|
// onTap:
|
|
// widget.isViewMode
|
|
// ? null
|
|
// : () async {
|
|
// await _selectCheckDateOfIssue(context);
|
|
// if (controllers["dateOfIssue"]!.text.isNotEmpty) {
|
|
// setState(() {
|
|
// // errorMessages.remove("start_date");
|
|
// });
|
|
// }
|
|
// },
|
|
// child: AbsorbPointer(
|
|
// child: TextField(
|
|
// controller: controllers["dateOfIssue"],
|
|
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
// decoration: InputDecoration(
|
|
// labelText: "Select Date",
|
|
// labelStyle: GoogleFonts.poppins(
|
|
// fontSize: 12,
|
|
// color: Colors.grey,
|
|
// ),
|
|
// floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
// border: InputBorder.none,
|
|
// contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
// suffixIcon: const Icon(
|
|
// Icons.calendar_today,
|
|
// size: 16,
|
|
// color: Color(0xFF8B8FB2),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// );
|
|
// }
|
|
//
|
|
// Widget buildDateOfExpiry() {
|
|
// DateTime? _selectedDateOfExpiry;
|
|
//
|
|
// Future<void> _selectCheckDateOfExpiry(BuildContext context) async {
|
|
// DateTime now = DateTime.now();
|
|
// DateTime today = DateTime(now.year, now.month, now.day);
|
|
// // DateTime today = DateTime(now.year now.day, now.month,);
|
|
//
|
|
// DateTime? validFromDate;
|
|
//
|
|
// try {
|
|
// String? fromDateText = controllers["dateOfIssue"]?.text.trim();
|
|
// print("dateOfIssue From Text: $fromDateText");
|
|
// if (fromDateText!.isNotEmpty) {
|
|
// validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText);
|
|
// print("dateOfIssue Valid From: $validFromDate");
|
|
// }
|
|
// } catch (e) {
|
|
// print("Error parsing valid from date: $e");
|
|
// }
|
|
//
|
|
// // Use max(today, validFromDate) as firstDate
|
|
// // DateTime firstDate = today;
|
|
// DateTime firstDate = validFromDate ?? today;
|
|
//
|
|
// // Parse dateOfIssue safely
|
|
// // DateTime firstDate;
|
|
// // try {
|
|
// // firstDate = DateFormat(
|
|
// // "yyyy-MM-dd",
|
|
// // ).parse(controllers["dateOfIssue"]!.text);
|
|
// // } catch (e) {
|
|
// // firstDate = today; // fallback if parsing fails
|
|
// // }
|
|
//
|
|
// DateTime? pickedDate = await showDatePicker(
|
|
// context: context,
|
|
//
|
|
// // initialDate:
|
|
// // _selectedDateOfExpiry != null &&
|
|
// // _selectedDateOfExpiry!.isAfter(firstDate)
|
|
// // ? _selectedDateOfExpiry!
|
|
// // : today,
|
|
// initialDate: firstDate,
|
|
// firstDate: firstDate,
|
|
// // firstDate: DateTime(1900),
|
|
// lastDate: DateTime(2100),
|
|
// initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
// );
|
|
//
|
|
// if (pickedDate != null) {
|
|
// _selectedDateOfExpiry = pickedDate;
|
|
// controllers["dateOfExpiry"]?.text = DateFormat(
|
|
// "dd-MM-yyyy",
|
|
// ).format(pickedDate);
|
|
// }
|
|
//
|
|
// // if (pickedDate != null && pickedDate != _selectedDateOfExpiry) {
|
|
// // setState(() {
|
|
// // _selectedDateOfExpiry = pickedDate;
|
|
// // controllers["dateOfExpiry"]?.text = DateFormat(
|
|
// // 'dd-MM-yyyy',
|
|
// // ).format(pickedDate);
|
|
// //
|
|
// // // controllers["dateOfExpiry"]?.text =
|
|
// // // DateFormat('dd-MM-yyyy').format(pickedDate);
|
|
// // });
|
|
// // }
|
|
// }
|
|
//
|
|
// return Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// Text(
|
|
// "Date of Expiry",
|
|
// style: GoogleFonts.poppins(
|
|
// fontSize: 12,
|
|
// fontWeight: FontWeight.w600,
|
|
// color: Color(0xFF575A74),
|
|
// ),
|
|
// ),
|
|
// SizedBox(height: 5),
|
|
// CustomTextFieldUserTravellerWrapper(
|
|
// isFocused: false,
|
|
// isDesktop: widget.isDesktop,
|
|
// width:
|
|
// widget.isDesktop
|
|
// ? MediaQuery.of(context).size.width * 0.13
|
|
// : null,
|
|
// child: SizedBox(
|
|
// height: 40,
|
|
// child: GestureDetector(
|
|
// onTap:
|
|
// widget.isViewMode
|
|
// ? null
|
|
// : () async {
|
|
// await _selectCheckDateOfExpiry(context);
|
|
// if (controllers["dateOfExpiry"]!.text.isNotEmpty) {
|
|
// setState(() {
|
|
// // errorMessages.remove("start_date");
|
|
// });
|
|
// }
|
|
// },
|
|
// child: AbsorbPointer(
|
|
// child: TextField(
|
|
// controller: controllers["dateOfExpiry"],
|
|
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
// decoration: InputDecoration(
|
|
// labelText: "Select Date",
|
|
// labelStyle: GoogleFonts.poppins(
|
|
// fontSize: 12,
|
|
// color: Colors.grey,
|
|
// ),
|
|
// floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
// border: InputBorder.none,
|
|
// contentPadding: const EdgeInsets.symmetric(vertical: 16),
|
|
// suffixIcon: const Icon(
|
|
// Icons.calendar_today,
|
|
// size: 16,
|
|
// color: Color(0xFF8B8FB2),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// );
|
|
// }
|
|
|
|
Widget buildPassportDocument() {
|
|
// void pickPDFWeb() {
|
|
// html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
|
|
// uploadInput.accept = '.pdf';
|
|
// uploadInput.click();
|
|
//
|
|
// uploadInput.onChange.listen((e) {
|
|
// final file = uploadInput.files!.first;
|
|
//
|
|
// // Ensure the file is a PDF
|
|
// if (!file.type.contains("pdf")) {
|
|
// print("Error: Not a PDF file");
|
|
// return;
|
|
// }
|
|
//
|
|
// // 🔹 File size check: Ensure it does not exceed 3MB
|
|
// int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
|
|
// if (file.size > maxFileSize) {
|
|
// print('Error: File size exceeds 3MB');
|
|
// return;
|
|
// }
|
|
//
|
|
// setState(() {
|
|
// selectedFileNames = file.name;
|
|
// passportFile = file;
|
|
// passportFileUrlFromApi = null;
|
|
// });
|
|
//
|
|
// print('PDF File selected: ${file.name}');
|
|
// });
|
|
// }
|
|
|
|
void pickFileWeb() {
|
|
html.FileUploadInputElement uploadInput = html.FileUploadInputElement();
|
|
// Accept PDFs and common image formats
|
|
uploadInput.accept = '.pdf,.jpg,.jpeg,.png';
|
|
uploadInput.click();
|
|
|
|
uploadInput.onChange.listen((e) {
|
|
final file = uploadInput.files!.first;
|
|
|
|
// Ensure the file is PDF or image
|
|
if (!(file.type.contains("pdf") || file.type.contains("image"))) {
|
|
print("Error: Not a PDF or image file");
|
|
return;
|
|
}
|
|
|
|
// File size check: Ensure it does not exceed 3MB
|
|
int maxFileSize = 3 * 1024 * 1024; // 3MB in bytes
|
|
if (file.size > maxFileSize) {
|
|
print('Error: File size exceeds 3MB');
|
|
return;
|
|
}
|
|
|
|
setState(() {
|
|
selectedFileNames = file.name;
|
|
passportFile = file;
|
|
passportFileUrlFromApi = null;
|
|
});
|
|
|
|
print('File selected: ${file.name}');
|
|
});
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Passport Document",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: GestureDetector(
|
|
onTap: pickFileWeb,
|
|
child: Container(
|
|
padding: EdgeInsets.symmetric(horizontal: 10),
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedFileNames ?? "Select Document",
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
overflow: TextOverflow.ellipsis, // Prevents overflow issues
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
SizedBox(height: 5),
|
|
Center(
|
|
child: Text(
|
|
" * Allow types pdf, jpg, jpeg, png and max size 3MB ",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 10,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
),
|
|
SizedBox(height: 10),
|
|
if (passportFile != null || passportFileUrlFromApi != null)
|
|
// Centers the text
|
|
Container(
|
|
color: Colors.white,
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.25
|
|
: MediaQuery.of(context).size.width * 0.8,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
GestureDetector(
|
|
onTap: () {
|
|
print('DOWNLOAD - $passportFile - ${widget.userIdApi} ');
|
|
|
|
if (passportFile != null) {
|
|
try {
|
|
// ✅ Step 1: Create a Blob directly from the file
|
|
// final blob =
|
|
// html.Blob([passportFile!], 'application/pdf');
|
|
//
|
|
// // ✅ Step 2: Generate a download URL from the Blob
|
|
// final url = html.Url.createObjectUrlFromBlob(blob);
|
|
//
|
|
// // ✅ Step 3: Create an invisible anchor to trigger download
|
|
// final anchor = html.AnchorElement(href: url)
|
|
// ..setAttribute(
|
|
// "download", selectedFileNames ?? "document.pdf")
|
|
// ..style.display = "none";
|
|
//
|
|
// // ✅ Step 4: Add anchor to DOM and click it
|
|
// html.document.body!.append(anchor);
|
|
// anchor.click();
|
|
//
|
|
// // ✅ Step 5: Clean up
|
|
// anchor.remove();
|
|
// html.Url.revokeObjectUrl(url);
|
|
|
|
// Create a blob from the response body
|
|
final blob = html.Blob([passportFile!]);
|
|
|
|
// Generate a download URL for the blob
|
|
final url = html.Url.createObjectUrlFromBlob(blob);
|
|
|
|
// Create a link element to trigger the download
|
|
final anchor =
|
|
html.AnchorElement(href: url)
|
|
..setAttribute(
|
|
'download',
|
|
selectedFileNames ?? "document.pdf",
|
|
)
|
|
..click();
|
|
|
|
// Revoke the download URL to free up resources
|
|
html.Url.revokeObjectUrl(url);
|
|
|
|
print("Download triggered successfully!");
|
|
} catch (e) {
|
|
print("Error during download: $e");
|
|
}
|
|
} else if (passportFileUrlFromApi != null) {
|
|
print("Raw file path: $passportFileUrlFromApi");
|
|
apiService.getPassportDocDownload(
|
|
context,
|
|
widget.userIdApi,
|
|
);
|
|
|
|
// // Extract public part of the path starting from "/assets"
|
|
// String cleanedPath = passportFileUrlFromApi!;
|
|
// final index = passportFileUrlFromApi!.indexOf("/assets");
|
|
// if (index != -1) {
|
|
// cleanedPath = passportFileUrlFromApi!.substring(index);
|
|
// }
|
|
//
|
|
// final fullUrl =
|
|
// 'https://apitest.tripapprovaltool.com$cleanedPath';
|
|
// print("Final download URL: $fullUrl");
|
|
//
|
|
// final anchor =
|
|
// html.AnchorElement(href: fullUrl)
|
|
// ..target = '_blank'
|
|
// ..download = selectedFileNames ?? "document.pdf"
|
|
// ..click();
|
|
} else {
|
|
print("No file available to download.");
|
|
}
|
|
},
|
|
child: Container(
|
|
padding: const EdgeInsets.all(5),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(5),
|
|
color: Colors.green.shade300,
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
Text(
|
|
"Download",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w200,
|
|
color: Colors.white,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
textAlign:
|
|
TextAlign
|
|
.end, // Ensures text is centered within the Text widget
|
|
),
|
|
SizedBox(width: 5),
|
|
Icon(Icons.download, size: 13, color: Colors.white),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
//------------------------Local Details----------------------------------------
|
|
|
|
Widget _buildLocalIdDetails(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(height: 10),
|
|
_buildLocalDetailDataRow1(widget.isDesktop),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildLocalDetailDataRow1(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildLocalIdType(),
|
|
Spacer(),
|
|
buildIdNumber(),
|
|
Spacer(),
|
|
buildFullNameAsId(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildLocalIdType(),
|
|
SizedBox(height: 8),
|
|
buildIdNumber(),
|
|
SizedBox(height: 8), // Vertical space
|
|
|
|
buildFullNameAsId(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildIdNumber() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Id Number",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: controllers["local_id_num"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("local_id_num");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Id Number",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildLocalIdType() {
|
|
final List<Map<String, dynamic>> localIdOptions = [
|
|
{"dropdown_value": "Licence"},
|
|
{"dropdown_value": "PAN"},
|
|
{"dropdown_value": "Voter Card"},
|
|
{"dropdown_value": "Aadhar Card"},
|
|
];
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Id Type",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 35,
|
|
width: double.infinity,
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: false,
|
|
fit: FlexFit.loose,
|
|
menuProps: const MenuProps(backgroundColor: Colors.white),
|
|
itemBuilder: (context, item, isSelected) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 8,
|
|
),
|
|
child: Text(
|
|
item['dropdown_value'] ?? 'Select',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
dropdownDecoratorProps: const DropDownDecoratorProps(
|
|
dropdownSearchDecoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
hintText: "Select",
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 5,
|
|
),
|
|
),
|
|
),
|
|
dropdownButtonProps: const DropdownButtonProps(
|
|
icon: Icon(Icons.arrow_drop_down),
|
|
),
|
|
dropdownBuilder: (context, selectedItem) {
|
|
if (selectedItem == null || selectedItem.isEmpty) {
|
|
return Text(
|
|
"Select ", // fallback text
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
);
|
|
}
|
|
return Text(
|
|
selectedItem['dropdown_value'] ?? "Select ",
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
);
|
|
},
|
|
items: localIdOptions,
|
|
selectedItem:
|
|
selectedLocalIdType == null
|
|
? null
|
|
: {"dropdown_value": selectedLocalIdType},
|
|
onChanged:
|
|
widget.isViewMode
|
|
? null
|
|
: (Map<String, dynamic>? newItem) {
|
|
if (newItem != null) {
|
|
setState(() {
|
|
selectedLocalIdType = newItem['dropdown_value'];
|
|
});
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildFullNameAsId() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Full Name As ID",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: controllers["full_name_as_id"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("full_name_as_id");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "FullName As ID",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
//------------------------Preferences Domestic ---------------------------------------
|
|
|
|
Widget _buildPreferencesDomestic(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(height: 10),
|
|
_buildDomesticDetailDataRow1(widget.isDesktop),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildDomesticDetailDataRow1(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildDomesticSeatType(),
|
|
SizedBox(width: 15),
|
|
buildDomesticMealPref(),
|
|
SizedBox(width: 15),
|
|
buildDomesticAddtnlPref(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildDomesticSeatType(),
|
|
SizedBox(height: 8),
|
|
buildDomesticMealPref(),
|
|
SizedBox(height: 8), // Vertical space
|
|
|
|
buildDomesticAddtnlPref(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildDomesticSeatType() {
|
|
final List<Map<String, dynamic>> seatOptions = [
|
|
{"dropdown_value": "Aisle"},
|
|
{"dropdown_value": "Window"},
|
|
{"dropdown_value": "Middle"},
|
|
];
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Seat Preference",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.15
|
|
: null,
|
|
isFocused: _dSeatPrefFocus,
|
|
padding: const EdgeInsets.symmetric(horizontal: 0),
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 35,
|
|
width: double.infinity,
|
|
child: Focus(
|
|
focusNode: _dSeatPrefFocusNode,
|
|
onFocusChange: (hasFocus) {
|
|
setState(() {
|
|
_dSeatPrefFocus = hasFocus;
|
|
});
|
|
},
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
// Request focus when user taps
|
|
_dSeatPrefFocusNode.requestFocus();
|
|
},
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: false,
|
|
fit: FlexFit.loose,
|
|
menuProps: const MenuProps(backgroundColor: Colors.white),
|
|
itemBuilder: (context, item, isSelected) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 8,
|
|
),
|
|
child: Text(
|
|
item['dropdown_value'] ?? "Select Seat",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
dropdownDecoratorProps: DropDownDecoratorProps(
|
|
dropdownSearchDecoration: InputDecoration(
|
|
// border: InputBorder.none,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide(
|
|
color:
|
|
(_dSeatPrefFocus) ? (layoutColor) : Colors.white,
|
|
width: 0.5,
|
|
),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color:
|
|
(_dSeatPrefFocus) ? (layoutColor) : Colors.white,
|
|
width: 0.5,
|
|
// const Color(0xFFD6D5E6),
|
|
),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: (layoutColor),
|
|
width: 0.5,
|
|
),
|
|
),
|
|
hintText: "Select Seat",
|
|
hintStyle: GoogleFonts.poppins(
|
|
fontSize: 11.5,
|
|
color: Colors.grey,
|
|
),
|
|
contentPadding: const EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 5,
|
|
),
|
|
),
|
|
),
|
|
dropdownButtonProps: const DropdownButtonProps(
|
|
icon: Icon(Icons.arrow_drop_down),
|
|
),
|
|
dropdownBuilder: (context, selectedItem) {
|
|
if (selectedItem == null ||
|
|
selectedItem.isEmpty ||
|
|
selectedItem['dropdown_value'].toString().isEmpty) {
|
|
return Text(
|
|
"Select Seat", // fallback text
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
);
|
|
}
|
|
return Text(
|
|
selectedItem['dropdown_value'] ?? 'Select Seat',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
);
|
|
},
|
|
items: seatOptions,
|
|
selectedItem:
|
|
selectedDomesticSeat == null
|
|
? null
|
|
: {"dropdown_value": selectedDomesticSeat},
|
|
onChanged:
|
|
widget.isViewMode
|
|
? null
|
|
: (Map<String, dynamic>? newItem) {
|
|
if (newItem != null) {
|
|
setState(() {
|
|
selectedDomesticSeat =
|
|
newItem['dropdown_value'];
|
|
});
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildDomesticMealPref() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Meal Preference",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.15
|
|
: null,
|
|
isFocused: focusStates["d_meal_prefFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
focusNode: focusNodes["d_meal_prefFocusNode"],
|
|
controller: controllers["d_meal_pref"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("d_meal_pref");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Meal Preference",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildDomesticAddtnlPref() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Additional Information",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop ? MediaQuery.of(context).size.width * 0.4 : null,
|
|
isFocused: focusStates["d_additonal_InfoFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: controllers["d_additonal_Info"],
|
|
focusNode: focusNodes["d_additonal_InfoFocusNode"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("d_additonal_Info");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Please Enter",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
//------------------------Preferences International ---------------------------------------
|
|
|
|
Widget _buildPreferencesInternational(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(height: 10),
|
|
_buildInternationalDetailDataRow1(widget.isDesktop),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildInternationalDetailDataRow1(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildInternationalSeatType(),
|
|
SizedBox(width: 15),
|
|
buildInternationalMealPref(),
|
|
SizedBox(width: 15),
|
|
buildInternationalAddtnlPref(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildInternationalSeatType(),
|
|
SizedBox(height: 8),
|
|
buildInternationalMealPref(),
|
|
SizedBox(height: 8), // Vertical space
|
|
|
|
buildInternationalAddtnlPref(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildInternationalSeatType() {
|
|
final List<Map<String, dynamic>> seatOptionsInt = [
|
|
{"dropdown_value": "Aisle"},
|
|
{"dropdown_value": "Window"},
|
|
{"dropdown_value": "Middle"},
|
|
];
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Seat Preference",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.15
|
|
: null,
|
|
isFocused: _iSeatPrefFocus,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 35,
|
|
width: double.infinity,
|
|
child: Focus(
|
|
focusNode: _iSeatPrefFocusNode,
|
|
onFocusChange: (hasFocus) {
|
|
setState(() {
|
|
_iSeatPrefFocus = hasFocus;
|
|
});
|
|
},
|
|
child: GestureDetector(
|
|
onTap: () {
|
|
// Request focus when user taps
|
|
_iSeatPrefFocusNode.requestFocus();
|
|
},
|
|
child: DropdownSearch<Map<String, dynamic>>(
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: false,
|
|
fit: FlexFit.loose,
|
|
menuProps: const MenuProps(backgroundColor: Colors.white),
|
|
itemBuilder: (context, item, isSelected) {
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 8,
|
|
),
|
|
child: Text(
|
|
item['dropdown_value'] ?? 'Select Seat',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
dropdownDecoratorProps: DropDownDecoratorProps(
|
|
dropdownSearchDecoration: InputDecoration(
|
|
// border: InputBorder.none,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
borderSide: BorderSide(
|
|
color:
|
|
(_iSeatPrefFocus) ? (layoutColor) : Colors.white,
|
|
width: 0.5,
|
|
),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color:
|
|
(_iSeatPrefFocus) ? (layoutColor) : Colors.white,
|
|
width: 0.5,
|
|
// const Color(0xFFD6D5E6),
|
|
),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderSide: BorderSide(
|
|
color: (layoutColor),
|
|
width: 0.5,
|
|
),
|
|
),
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 1,
|
|
),
|
|
),
|
|
),
|
|
// selectedItem: purposeList.cast<Map<String, dynamic>>().firstWhere(
|
|
// (item) => item['dropdown_key'] == _selectedIsBillable,
|
|
// orElse: () => {},
|
|
// ),
|
|
dropdownButtonProps: const DropdownButtonProps(
|
|
icon: Icon(Icons.arrow_drop_down),
|
|
),
|
|
|
|
dropdownBuilder: (context, selectedItem) {
|
|
if (selectedItem == null ||
|
|
selectedItem.isEmpty ||
|
|
selectedItem['dropdown_value'].toString().isEmpty) {
|
|
return Text(
|
|
"Select Seat", // fallback text
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
);
|
|
}
|
|
return Text(
|
|
selectedItem['dropdown_value'] ?? 'Select Seat',
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
);
|
|
},
|
|
items: seatOptionsInt,
|
|
selectedItem:
|
|
selectedIntenationalSeat == null
|
|
? null
|
|
: {"dropdown_value": selectedIntenationalSeat},
|
|
onChanged:
|
|
widget.isViewMode
|
|
? null
|
|
: (Map<String, dynamic>? newItem) {
|
|
if (newItem != null) {
|
|
setState(() {
|
|
selectedIntenationalSeat =
|
|
newItem['dropdown_value'];
|
|
});
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildInternationalMealPref() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Meal Preference",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.15
|
|
: null,
|
|
isFocused: focusStates["i_meal_prefFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: controllers["i_meal_pref"],
|
|
focusNode: focusNodes["i_meal_prefFocusNode"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("i_meal_pref");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Meal Preference",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildInternationalAddtnlPref() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Additional Information",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop ? MediaQuery.of(context).size.width * 0.4 : null,
|
|
isFocused: focusStates["i_additonal_InfoFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: controllers["i_additonal_Info"],
|
|
focusNode: focusNodes["i_additonal_InfoFocusNode"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("i_additonal_Info");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Please Enter",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
//--------------------------Other Details-------------------------------
|
|
|
|
Widget _buildOtherDetails(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [SizedBox(height: 10), buildEmergencyContactNum()],
|
|
);
|
|
}
|
|
|
|
Widget buildEmergencyContactNum() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Emergency Contact Number",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
// isFocused: false,
|
|
isFocused: focusStates["emergency_contactFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: controllers["emergency_contact"],
|
|
focusNode: focusNodes["emergency_contactFocusNode"],
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(RegExp(r'[0-9\s]')),
|
|
],
|
|
// maxLength: 10,
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("emergency_contact");
|
|
},
|
|
decoration: InputDecoration(
|
|
// counterText: '',
|
|
labelText: "Contact Number",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
//-------------------------Forex Prepaid Card Number------------------------
|
|
|
|
Widget _buildForexDetails(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [SizedBox(height: 10), _buildForexDetailDataRow1(isDesktop)],
|
|
);
|
|
}
|
|
|
|
Widget _buildForexDetailDataRow1(bool isDesktop) {
|
|
return Container(
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildForexCardNumber(),
|
|
SizedBox(width: 15),
|
|
buildForexExpiryDate(),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildForexCardNumber(),
|
|
SizedBox(height: 8),
|
|
buildForexExpiryDate(),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildForexCardNumber() {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Forex Pre-Paid Card Number",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: focusStates["forex_card_numFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: controllers["forex_card_num"],
|
|
focusNode: focusNodes["forex_card_numFocusNode"],
|
|
inputFormatters: [
|
|
FilteringTextInputFormatter.allow(RegExp(r'[0-9\s]')),
|
|
LengthLimitingTextInputFormatter(16),
|
|
],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
_clearError("forex_pre_paid_card_number");
|
|
apiCheckDuplicate(
|
|
"Forex Pre-Paid Card Number",
|
|
"forex_pre_paid_card_number",
|
|
value,
|
|
widget.userIdApi,
|
|
);
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Card Number",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
if (errorMessages["forex_pre_paid_card_number"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
errorMessages["forex_pre_paid_card_number"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 12),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildForexExpiryDate() {
|
|
DateTime? _selectedExpiryDate;
|
|
|
|
Future<void> _selectExpiryDate(BuildContext context) async {
|
|
DateTime now = DateTime.now();
|
|
DateTime today = DateTime(now.year, now.month, now.day);
|
|
|
|
DateTime? pickedDate = await showDatePicker(
|
|
context: context,
|
|
initialDate:
|
|
_selectedExpiryDate != null && _selectedExpiryDate!.isAfter(today)
|
|
? _selectedExpiryDate!
|
|
: today,
|
|
firstDate: DateTime(1900),
|
|
lastDate: DateTime(2100),
|
|
initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
);
|
|
|
|
if (pickedDate != null && pickedDate != _selectedExpiryDate) {
|
|
setState(() {
|
|
_selectedExpiryDate = pickedDate;
|
|
// controllers["forex_expiry_date"]?.text =
|
|
// DateFormat('yyyy-MM-dd').format(pickedDate);
|
|
controllers["forex_expiry_date"]?.text = DateFormat(
|
|
'dd-MM-yyyy',
|
|
).format(pickedDate);
|
|
});
|
|
}
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Forex Expiry Date",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: focusStates["forex_expiry_dateFocused"] ?? false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: GestureDetector(
|
|
onTap:
|
|
widget.isViewMode
|
|
? null
|
|
: () async {
|
|
focusNodes["forex_expiry_dateFocusNode"]
|
|
?.requestFocus();
|
|
await _selectExpiryDate(context);
|
|
},
|
|
child: AbsorbPointer(
|
|
child: TextField(
|
|
controller: controllers["forex_expiry_date"],
|
|
focusNode: focusNodes["forex_expiry_dateFocusNode"],
|
|
readOnly: true,
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
decoration: InputDecoration(
|
|
labelText: "Select Date",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
suffixIcon: Icon(
|
|
Icons.calendar_today,
|
|
size: 16,
|
|
color: Color(0xFF8B8FB2),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
//-------------------------Frequent Flier Information -------------------------------
|
|
|
|
Widget _buildFrequentFlierInfo(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
...frequentFlierEntries
|
|
.where((e) => e["is_active"] != "0")
|
|
.map((e) => _buildFrequentAirlineDataRow1(e, isDesktop)),
|
|
IconForAirlineMem(),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildFrequentAirlineDataRow1(
|
|
Map<String, dynamic> entry,
|
|
bool isDesktop,
|
|
) {
|
|
int localId = entry["local_id"];
|
|
return Container(
|
|
color: Colors.white,
|
|
padding: const EdgeInsets.only(top: 8.0),
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildAirline(entry),
|
|
SizedBox(width: 25),
|
|
buildFrequentFlierInformation(entry),
|
|
SizedBox(width: 15),
|
|
SizedBox(
|
|
height: 60,
|
|
child: Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.only(
|
|
top: 20,
|
|
left: 8,
|
|
), // 👈 Add top padding here
|
|
child: IconButton(
|
|
icon: Icon(
|
|
Icons.remove_circle_sharp,
|
|
color: Colors.red,
|
|
size: 15,
|
|
),
|
|
tooltip: 'Cancel the Details',
|
|
onPressed: () {
|
|
setState(() {
|
|
removeFrequentFlierEntry(entry);
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildAirline(entry),
|
|
SizedBox(height: 8),
|
|
buildFrequentFlierInformation(entry),
|
|
SizedBox(width: 15),
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.remove_circle_sharp,
|
|
color: Colors.red,
|
|
size: 15,
|
|
),
|
|
tooltip: 'Cancel the Details',
|
|
onPressed: () {
|
|
setState(() {
|
|
removeFrequentFlierEntry(entry);
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildAirline(Map<String, dynamic> entry) {
|
|
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
|
late List<String> countryCodes; // List of country codes
|
|
|
|
// countryList = [];
|
|
bool hasAirlineCountryData =
|
|
apiAirlineCountryData == null || apiAirlineCountryData!.isEmpty;
|
|
|
|
countryList = apiAirlineCountryData!;
|
|
print("TestcountryList2 - $countryList");
|
|
|
|
// countryMap = {
|
|
// for (var country in countryList)
|
|
// country['Code'] as String: '${country['City']} - ${country['Airport']}',
|
|
// };
|
|
|
|
countryMap = {
|
|
for (var country in countryList)
|
|
country['airline'] as String:
|
|
'${country['airline_name']} (${country['airline']})',
|
|
// country['airline'] as String: '${country['airline_name']}',
|
|
};
|
|
// airline
|
|
|
|
countryCodes = countryMap.keys.toList();
|
|
|
|
// setState(() {
|
|
// countryMap = tempCountryMap; // Update the map
|
|
// isCountryLoading = false;
|
|
// });
|
|
|
|
// Map country codes to country names
|
|
// countryMap = {
|
|
//
|
|
//
|
|
// for (var item in countryList)
|
|
// item['country_code'] as String: item['country_name'] as String
|
|
// };
|
|
|
|
// Extract only country codes for processing
|
|
|
|
//--------------------------
|
|
|
|
// String? selectedCountry = entry['airline'];
|
|
String? selectedCode = entry['airline'];
|
|
String? selectedText =
|
|
selectedCode != null ? countryMap[selectedCode] : null;
|
|
|
|
print("selectedCode: $selectedCode");
|
|
print("selectedText: $selectedText");
|
|
print(
|
|
"items.contains(selectedText): ${countryMap.values.contains(selectedText)}",
|
|
);
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Airline",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
// width:
|
|
// widget.isDesktop
|
|
// ? MediaQuery.of(context).size.width * 0.17
|
|
// : null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child:
|
|
hasAirlineCountryData
|
|
? CircularProgressIndicator()
|
|
: DropdownSearch<String>(
|
|
// selectedItem: countryMap[selectedCountry],
|
|
// selectedItem: entry['airline'] != null
|
|
// ? countryMap[entry['airline']]
|
|
// : null,
|
|
// selectedItem: countryMap.containsKey(entry['airline'])
|
|
// ? countryMap[entry['airline']]
|
|
// : null,
|
|
// selectedItem: countryMap[selectedCode],
|
|
// selectedItem: countryMap.containsKey(selectedCode)
|
|
// ? countryMap[selectedCode]
|
|
// : null,
|
|
selectedItem:
|
|
(entry["airline"] != null &&
|
|
countryMap.containsKey(entry["airline"]))
|
|
? countryMap[entry["airline"]]
|
|
: null,
|
|
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: true, // Enables search functionality
|
|
menuProps: const MenuProps(
|
|
backgroundColor: Colors.white,
|
|
),
|
|
constraints: BoxConstraints(maxHeight: 250),
|
|
itemBuilder: (context, item, isSelected) {
|
|
// Extract airline name and code from "AirlineName (CODE)"
|
|
final match = RegExp(
|
|
r'^(.*)\s+\(([^)]+)\)$',
|
|
).firstMatch(item);
|
|
final name = match?.group(1) ?? '';
|
|
final code = match?.group(2) ?? '';
|
|
|
|
return Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8.0,
|
|
vertical: 6.0,
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
child: Text(
|
|
name,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
Text(
|
|
code,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: Colors.grey,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
},
|
|
|
|
// itemBuilder:
|
|
// (context, item, isSelected) => Padding(
|
|
// padding: const EdgeInsets.symmetric(
|
|
// horizontal: 8.0,
|
|
// vertical: 6.0,
|
|
// ),
|
|
// child: Text(
|
|
// item,
|
|
// style: GoogleFonts.poppins(fontSize: 11.5),
|
|
// ),
|
|
// ),
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search Airline...",
|
|
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
|
|
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(
|
|
// Center-align selected item
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedItem ?? "Select Airline",
|
|
style: TextStyle(fontSize: 12),
|
|
),
|
|
),
|
|
onChanged: (String? newValue) {
|
|
setState(() {
|
|
// Find the country_code based on selected country_name
|
|
// selectedCountry = countryMap.entries
|
|
// .firstWhere((entry) => entry.value == newValue)
|
|
// .key;
|
|
|
|
final selectedCode =
|
|
countryMap.entries
|
|
.firstWhere(
|
|
(entry) => entry.value == newValue,
|
|
)
|
|
.key;
|
|
entry['airline'] = selectedCode;
|
|
|
|
// final selectedCode = countryMap.entries
|
|
// .firstWhere((entry) => entry.value == newValue)
|
|
// .key;
|
|
// entry['airline'] = selectedCode;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
|
|
// CustomTextFieldUserTravellerWrapper(
|
|
// width: widget.isDesktop
|
|
// ? MediaQuery.of(context).size.width * 0.15
|
|
// : null,
|
|
// isFocused: false,
|
|
// isDesktop: widget.isDesktop,
|
|
// child: SizedBox(
|
|
// height: 40,
|
|
// child: TextField(
|
|
// style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
// controller: entry["controller_airline"],
|
|
// enabled: !widget.isViewMode,
|
|
// onChanged: (value) {
|
|
// _clearError("first_name");
|
|
// },
|
|
// decoration: InputDecoration(
|
|
// labelText: "Airline",
|
|
// labelStyle:
|
|
// GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
|
|
// floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
// border: InputBorder.none,
|
|
// contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildFrequentFlierInformation(Map<String, dynamic> entry) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Frequent Flyer Information",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: entry["controller_flier_number"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
// _clearError("first_name");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Frequent Flyer Information",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget IconForAirlineMem() {
|
|
return Container(
|
|
// height: 60, // must define height
|
|
alignment: Alignment.bottomRight,
|
|
child: IconButton(
|
|
icon: Icon(Icons.add_circle, color: Color(0xFF114D8B), size: 30),
|
|
onPressed: () => addFrequentFlierEntry(),
|
|
tooltip: "Frequent Flyer Information",
|
|
),
|
|
);
|
|
}
|
|
|
|
//-------------------------Frequent Loyalty MemberShip Hotels -------------------------------
|
|
|
|
Widget _buildHotelLoyalty(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// ...hotelLoyaltyEntries.map((entry) {
|
|
// return _buildFrequentLoyaltyDataRow1(entry, isDesktop);
|
|
// }),
|
|
...hotelLoyaltyEntries
|
|
.where((e) => e["is_active"] != "0")
|
|
.map((e) => _buildFrequentLoyaltyDataRow1(e, isDesktop)),
|
|
|
|
IconForLoyaltyMem(),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildFrequentLoyaltyDataRow1(
|
|
Map<String, dynamic> entry,
|
|
bool isDesktop,
|
|
) {
|
|
int localId = entry["local_id"];
|
|
return Container(
|
|
padding: const EdgeInsets.only(top: 8.0),
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildHotel(entry),
|
|
SizedBox(width: 25),
|
|
buildHotelMembershipNum(entry),
|
|
SizedBox(
|
|
height: 60,
|
|
child: Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.only(
|
|
top: 20,
|
|
left: 8,
|
|
), // 👈 Add top padding here
|
|
child: IconButton(
|
|
icon: Icon(
|
|
Icons.remove_circle_sharp,
|
|
color: Colors.red,
|
|
size: 15,
|
|
),
|
|
tooltip: 'Cancel the Details',
|
|
onPressed: () {
|
|
setState(() {
|
|
removeHotelLoyaltyEntry(entry);
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildHotel(entry),
|
|
SizedBox(height: 8),
|
|
buildHotelMembershipNum(entry),
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.remove_circle_sharp,
|
|
color: Colors.red,
|
|
size: 15,
|
|
),
|
|
tooltip: 'Cancel the Details',
|
|
onPressed: () {
|
|
setState(() {
|
|
// removeHotelLoyaltyEntry(localId);
|
|
removeHotelLoyaltyEntry(entry);
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildHotel(Map<String, dynamic> entry) {
|
|
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
|
late List<String> countryCodes; // List of country codes
|
|
|
|
bool hasAirlineCountryData =
|
|
apiHotelsData == null || apiHotelsData!.isEmpty;
|
|
|
|
countryList = apiHotelsData!;
|
|
print("TestcountryList2 - $countryList");
|
|
|
|
countryMap = {
|
|
for (var country in countryList)
|
|
country['hotel_id'] as String: '${country['hotel_name']}',
|
|
};
|
|
|
|
countryCodes = countryMap.keys.toList();
|
|
String? selectedCode = entry['hotel_id'];
|
|
// String? selectedCode = entry['hotel_id'];
|
|
String? selectedText =
|
|
selectedCode != null ? countryMap[selectedCode] : null;
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Hotel",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
// width: widget.isDesktop
|
|
// ? MediaQuery.of(context).size.width * 0.15
|
|
// : null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child:
|
|
hasAirlineCountryData
|
|
? CircularProgressIndicator()
|
|
: DropdownSearch<String>(
|
|
selectedItem:
|
|
(entry["hotel_id"] != null &&
|
|
countryMap.containsKey(entry["hotel_id"]))
|
|
? countryMap[entry["hotel_id"]]
|
|
: null,
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: true, // Enables search functionality
|
|
|
|
menuProps: const MenuProps(
|
|
backgroundColor: Colors.white,
|
|
),
|
|
constraints: BoxConstraints(maxHeight: 250),
|
|
itemBuilder:
|
|
(context, item, isSelected) => Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8.0,
|
|
vertical: 6.0,
|
|
),
|
|
child: Text(
|
|
item,
|
|
style: GoogleFonts.poppins(fontSize: 11.5),
|
|
),
|
|
),
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search Hotels...",
|
|
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
|
|
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(
|
|
// Center-align selected item
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedItem ?? "Select Hotels",
|
|
style: GoogleFonts.poppins(fontSize: 11),
|
|
),
|
|
),
|
|
onChanged: (String? newValue) {
|
|
setState(() {
|
|
// Find the country_code based on selected country_name
|
|
// selectedCountry = countryMap.entries
|
|
// .firstWhere((entry) => entry.value == newValue)
|
|
// .key;
|
|
|
|
final selectedCode =
|
|
countryMap.entries
|
|
.firstWhere(
|
|
(entry) => entry.value == newValue,
|
|
)
|
|
.key;
|
|
|
|
print("SelectedHotelId - $selectedCode");
|
|
entry['hotel_id'] = selectedCode;
|
|
// entry["controller_hotel"] = newValue;
|
|
entry['hotel_name'] = newValue;
|
|
// final selectedCode = countryMap.entries
|
|
// .firstWhere((entry) => entry.value == newValue)
|
|
// .key;
|
|
// entry['airline'] = selectedCode;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildHotelMembershipNum(Map<String, dynamic> entry) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Hotel Membership Number",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: TextField(
|
|
style: GoogleFonts.poppins(fontSize: 12, color: Colors.black),
|
|
controller: entry["controller_membership"],
|
|
enabled: !widget.isViewMode,
|
|
onChanged: (value) {
|
|
// _clearError("first_name");
|
|
},
|
|
decoration: InputDecoration(
|
|
labelText: "Hotel Membership Number",
|
|
labelStyle: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.grey,
|
|
),
|
|
floatingLabelBehavior: FloatingLabelBehavior.never,
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(vertical: 16),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget IconForLoyaltyMem() {
|
|
return Container(
|
|
height: 60, // must define height
|
|
alignment: Alignment.bottomRight,
|
|
child: IconButton(
|
|
icon: Icon(Icons.add_circle, color: Color(0xFF114D8B), size: 30),
|
|
onPressed: () => addHotelLoyaltyEntry(),
|
|
tooltip: "Add Hotel Loyalty Membership",
|
|
),
|
|
);
|
|
}
|
|
|
|
//-------------------------Visa Details -----------------------------------------
|
|
|
|
Widget _buildVisaDetails(bool isDesktop) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(height: 10),
|
|
|
|
...visaEntries
|
|
.where((e) => e["is_active"] != "0")
|
|
.map((e) => _buildVisaDataRow1(e, isDesktop)),
|
|
|
|
// _buildVisaDataRow1(isDesktop),
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Text(
|
|
" * confirm your passport is valid for next 6 months",
|
|
style: GoogleFonts.poppins(fontSize: 10),
|
|
),
|
|
IconForVisa(),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget _buildVisaDataRow1(Map<String, dynamic> entry, bool isDesktop) {
|
|
return Container(
|
|
padding: const EdgeInsets.only(top: 8),
|
|
color: Colors.white,
|
|
child:
|
|
widget.isDesktop
|
|
? Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildVisaCountry(entry),
|
|
SizedBox(width: 25),
|
|
buildVisaType(entry),
|
|
SizedBox(width: 15),
|
|
buildVisaValidFrom(entry),
|
|
SizedBox(width: 15),
|
|
buildVisaValidUpTo(entry),
|
|
|
|
SizedBox(
|
|
height: 60,
|
|
child: Center(
|
|
child: Padding(
|
|
padding: EdgeInsets.only(
|
|
top: 20,
|
|
left: 8,
|
|
), // 👈 Add top padding here
|
|
child: IconButton(
|
|
icon: Icon(
|
|
Icons.remove_circle_sharp,
|
|
color: Colors.red,
|
|
size: 15,
|
|
),
|
|
tooltip: 'Cancel the Details',
|
|
onPressed: () {
|
|
setState(() {
|
|
removeVisaEntry(entry);
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
buildVisaCountry(entry),
|
|
SizedBox(height: 8),
|
|
buildVisaType(entry),
|
|
SizedBox(height: 8),
|
|
buildVisaValidFrom(entry),
|
|
SizedBox(height: 8),
|
|
buildVisaValidUpTo(entry),
|
|
|
|
IconButton(
|
|
icon: Icon(
|
|
Icons.remove_circle_sharp,
|
|
color: Colors.red,
|
|
size: 15,
|
|
),
|
|
tooltip: 'Cancel the Details',
|
|
onPressed: () {
|
|
setState(() {
|
|
// visaRowIds.remove(id);
|
|
removeVisaEntry(entry);
|
|
});
|
|
},
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget buildVisaCountry(entry) {
|
|
late Map<String, String> countryMap; // Mapping country_code -> country_name
|
|
late List<String> countryCodes; // List of country codes
|
|
|
|
// countryList = [];
|
|
countryList = apiCountryData ?? [];
|
|
|
|
print("TestcountryList1 - $countryList");
|
|
|
|
// Map country codes to country names
|
|
countryMap = {
|
|
for (var item in countryList)
|
|
item['country_code'] as String: item['country_name'] as String,
|
|
};
|
|
|
|
// Extract only country codes for processing
|
|
countryCodes = countryMap.keys.toList();
|
|
|
|
// selectedCountry ??= null;
|
|
|
|
String? selectedCountry = entry['country_code'];
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Country",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.17
|
|
: null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: DropdownSearch<String>(
|
|
selectedItem: countryMap[selectedCountry],
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: true, // Enables search functionality
|
|
menuProps: const MenuProps(backgroundColor: Colors.white),
|
|
// constraints: BoxConstraints(maxHeight: 250),
|
|
itemBuilder:
|
|
(context, item, isSelected) => Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8.0,
|
|
vertical: 6.0,
|
|
),
|
|
child: Text(
|
|
item,
|
|
style: GoogleFonts.poppins(fontSize: 11.5),
|
|
),
|
|
),
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search Country...",
|
|
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
|
|
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(
|
|
// Center-align selected item
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedItem ?? "Select Country",
|
|
style: TextStyle(fontSize: 12),
|
|
),
|
|
),
|
|
onChanged: (String? newValue) {
|
|
setState(() {
|
|
// Find the country_code based on selected country_name
|
|
// selectedCountry = countryMap.entries
|
|
// .firstWhere((entry) => entry.value == newValue)
|
|
// .key;
|
|
|
|
final selectedCode =
|
|
countryMap.entries
|
|
.firstWhere((entry) => entry.value == newValue)
|
|
.key;
|
|
entry['country_code'] = selectedCode;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildVisaType(entry) {
|
|
if (apiData == null || apiData?['visa_type_of_visa'] == null) {
|
|
return Center(
|
|
child: Transform.scale(scale: 0.5, child: CircularProgressIndicator()),
|
|
);
|
|
}
|
|
|
|
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
|
|
|
|
// Map for id => name
|
|
Map<String, String> visaTypeMap = {
|
|
for (var item in purposeList)
|
|
item['dropdown_key'].toString(): item['dropdown_value'].toString(),
|
|
};
|
|
|
|
// Selected value from entry
|
|
String? selectedPurpose = entry['visa_type_id']?.toString();
|
|
|
|
if (!visaTypeMap.containsKey(selectedPurpose)) {
|
|
selectedPurpose = null;
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Visa Type",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.17
|
|
: null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: DropdownSearch<String>(
|
|
selectedItem: visaTypeMap[selectedPurpose],
|
|
enabled: purposeList.isNotEmpty,
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: true,
|
|
fit: FlexFit.loose,
|
|
menuProps: const MenuProps(backgroundColor: Colors.white),
|
|
constraints: BoxConstraints(maxHeight: 200),
|
|
itemBuilder:
|
|
(context, item, isSelected) => Container(
|
|
padding: EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
vertical: 6,
|
|
),
|
|
child: Text(
|
|
item,
|
|
style: GoogleFonts.poppins(fontSize: 11.5),
|
|
),
|
|
),
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search Visa Type...",
|
|
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
|
),
|
|
),
|
|
),
|
|
items: visaTypeMap.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 Visa Type",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
),
|
|
onChanged: (newValue) {
|
|
if (newValue == null) return;
|
|
|
|
final selectedId =
|
|
visaTypeMap.entries
|
|
.firstWhere((entry) => entry.value == newValue)
|
|
.key;
|
|
|
|
setState(() {
|
|
entry['visa_type_id'] = selectedId;
|
|
});
|
|
|
|
print("Updating form data: visa_type_id -> $selectedId");
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildVisaType3(entry) {
|
|
if (apiData == null || apiData?['visa_type_of_visa'] == null) {
|
|
return Center(
|
|
child: Transform.scale(scale: 0.5, child: CircularProgressIndicator()),
|
|
);
|
|
}
|
|
|
|
// List<dynamic> purposeList = apiData?['visa_type_of_visa'] ?? [];
|
|
// List<dynamic> purposeList = [];
|
|
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
|
|
|
|
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
|
|
// selectedPurpose ??=
|
|
// dropdownItems.isNotEmpty ? dropdownItems.first.value : null;
|
|
|
|
// String? selectedPurpose = entry['visa_type_id'];
|
|
|
|
// 3. Selected value
|
|
String? selectedPurpose = entry['visa_type_id']?.toString();
|
|
|
|
// 4. Avoid crash if selected value doesn't match any item
|
|
if (!dropdownItems.any((item) => item.value == selectedPurpose)) {
|
|
selectedPurpose = null;
|
|
}
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Visa Type",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.17
|
|
: null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: DropdownButtonFormField<String>(
|
|
value: selectedPurpose,
|
|
style: TextStyle(fontSize: 12),
|
|
decoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(
|
|
horizontal: 10,
|
|
), // Proper padding
|
|
),
|
|
hint: Text("Select"),
|
|
onChanged:
|
|
purposeList.isNotEmpty
|
|
? (newValue) {
|
|
setState(() {
|
|
// selectedPurpose = newValue;
|
|
entry['visa_type_id'] = newValue;
|
|
});
|
|
print(
|
|
"Updating form data: Flight -> trip_type -> ${newValue ?? ""}",
|
|
);
|
|
}
|
|
: null,
|
|
items: dropdownItems,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildVisaType2(entry) {
|
|
late Map<String, String>
|
|
visaTypeMap; // Mapping country_code -> country_name
|
|
late List<String> visaTypeCodes; // List of country codes
|
|
|
|
// List<dynamic> purposeList = apiData?['visa_type_of_visa'];
|
|
|
|
List<dynamic> purposeList = apiData?['visa_type_of_visa'];
|
|
|
|
print("purposeList - $purposeList");
|
|
|
|
visaTypeMap = {
|
|
for (var item in purposeList)
|
|
item['visa_type_id'] as String: item['visa_type_of_visa'] as String,
|
|
};
|
|
|
|
// Extract only country codes for processing
|
|
visaTypeCodes = visaTypeMap.keys.toList();
|
|
|
|
// selectedPurpose ??= null;
|
|
|
|
String? selectedPurpose = entry['visa_type_of_visa'];
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Visa Type",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.17
|
|
: null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: DropdownSearch<String>(
|
|
selectedItem: visaTypeMap[selectedPurpose],
|
|
popupProps: PopupProps.menu(
|
|
showSearchBox: true, // Enables search functionality
|
|
menuProps: const MenuProps(backgroundColor: Colors.white),
|
|
// constraints: BoxConstraints(maxHeight: 250),
|
|
itemBuilder:
|
|
(context, item, isSelected) => Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8.0,
|
|
vertical: 6.0,
|
|
),
|
|
child: Text(
|
|
item,
|
|
style: GoogleFonts.poppins(fontSize: 11.5),
|
|
),
|
|
),
|
|
searchFieldProps: TextFieldProps(
|
|
decoration: InputDecoration(
|
|
hintText: "Search Visa Type...",
|
|
hintStyle: GoogleFonts.poppins(fontSize: 11.5),
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 10),
|
|
),
|
|
),
|
|
),
|
|
items: visaTypeMap.values.toList(),
|
|
dropdownDecoratorProps: DropDownDecoratorProps(
|
|
dropdownSearchDecoration: InputDecoration(
|
|
border: InputBorder.none,
|
|
contentPadding: EdgeInsets.symmetric(horizontal: 1),
|
|
),
|
|
),
|
|
dropdownBuilder:
|
|
(context, selectedItem) => Align(
|
|
// Center-align selected item
|
|
alignment: Alignment.centerLeft,
|
|
child: Text(
|
|
selectedItem ?? "Select Visa Type",
|
|
style: TextStyle(fontSize: 12),
|
|
),
|
|
),
|
|
onChanged: (String? newValue) {
|
|
setState(() {
|
|
// Find the country_code based on selected country_name
|
|
// selectedCountry = countryMap.entries
|
|
// .firstWhere((entry) => entry.value == newValue)
|
|
// .key;
|
|
|
|
final selectedPurpose =
|
|
visaTypeMap.entries
|
|
.firstWhere((entry) => entry.value == newValue)
|
|
.key;
|
|
entry['visa_type_of_visa'] = selectedPurpose;
|
|
});
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
DateTime? _selectedCheckOutDate;
|
|
DateTime? _selectedCheckInDate;
|
|
|
|
Widget buildVisaValidFrom(Map<String, dynamic> entry) {
|
|
TimeOfDay? _selectedCheckOutTime;
|
|
|
|
Future<void> _selectValidFromDate(BuildContext context) async {
|
|
DateTime now = DateTime.now();
|
|
DateTime today = DateTime(now.year, now.month, now.day);
|
|
|
|
// Parse date from notifier if available, else use today
|
|
DateTime initialDate = today;
|
|
|
|
// Use previously selected date if valid
|
|
if (_selectedCheckOutDate != null &&
|
|
_selectedCheckOutDate!.isAfter(today)) {
|
|
initialDate = _selectedCheckOutDate!;
|
|
}
|
|
|
|
final pickedDate = await showDatePicker(
|
|
context: context,
|
|
// initialDate: initialDate,
|
|
// firstDate: initialDate,
|
|
firstDate: DateTime(1900),
|
|
// lastDate: DateTime(2100),
|
|
lastDate: today,
|
|
initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
);
|
|
|
|
print("CheckOut - $_selectedCheckOutDate");
|
|
print("CheckIn - $_selectedCheckInDate");
|
|
|
|
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
|
setState(() {
|
|
_selectedCheckInDate = pickedDate;
|
|
entry["controller_valid_from"]?.text = DateFormat(
|
|
'dd-MM-yyyy',
|
|
).format(pickedDate);
|
|
|
|
// Revalidate expiry
|
|
if (_selectedCheckOutDate != null &&
|
|
_selectedCheckOutDate!.isBefore(_selectedCheckInDate!)) {
|
|
print("Entry - $entry");
|
|
print("errorMessages - $errorMessages");
|
|
errorMessages["valid_upto${entry['local_id']}"] =
|
|
"Invalid Date Range";
|
|
// errorMessages["valid_upto"] = "Invalid Date Range";
|
|
} else {
|
|
errorMessages.remove("valid_upto${entry['local_id']}");
|
|
// errorMessages.remove("valid_upto");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Valid From",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.15
|
|
: null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: GestureDetector(
|
|
onTap: () async {
|
|
await _selectValidFromDate(context);
|
|
},
|
|
child: AbsorbPointer(
|
|
child: TextField(
|
|
// focusNode: _dateFocusNode,
|
|
controller: entry["controller_valid_from"],
|
|
readOnly: true,
|
|
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,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
Widget buildVisaValidUpTo(Map<String, dynamic> entry) {
|
|
TimeOfDay? _selectedCheckOutTime;
|
|
|
|
Future<void> _selectValidUpToDate(BuildContext context) async {
|
|
DateTime now = DateTime.now();
|
|
DateTime today = DateTime(now.year, now.month, now.day);
|
|
print("buildVisaValidUpTo ");
|
|
print("buildVisaValidUpTo - ${entry["controller_valid_from"].text}");
|
|
|
|
// Parse date from notifier if available, else use today
|
|
DateTime initialDate = today;
|
|
|
|
// Use previously selected date if valid
|
|
// if (_selectedCheckOutDate != null &&
|
|
// _selectedCheckOutDate!.isAfter(today)) {
|
|
// initialDate = _selectedCheckOutDate!;
|
|
// }
|
|
|
|
DateTime? validFromDate;
|
|
|
|
try {
|
|
String fromDateText = entry["controller_valid_from"].text.trim();
|
|
print("Valid From Text: $fromDateText");
|
|
if (fromDateText.isNotEmpty) {
|
|
validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText);
|
|
print("Parsed Valid From: $validFromDate");
|
|
}
|
|
} catch (e) {
|
|
print("Error parsing valid from date: $e");
|
|
}
|
|
|
|
// Use max(today, validFromDate) as firstDate
|
|
// DateTime firstDate = today;
|
|
DateTime firstDate = validFromDate ?? today;
|
|
if (validFromDate != null && validFromDate.isAfter(today)) {
|
|
firstDate = validFromDate;
|
|
}
|
|
final pickedDate = await showDatePicker(
|
|
context: context,
|
|
// initialDate: initialDate,
|
|
// firstDate: initialDate,
|
|
// firstDate: DateTime(1900),
|
|
initialDate: firstDate,
|
|
firstDate: firstDate,
|
|
lastDate: DateTime(2100),
|
|
initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
);
|
|
|
|
if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
|
setState(() {
|
|
_selectedCheckOutDate = pickedDate;
|
|
entry["controller_valid_upto"].text = DateFormat(
|
|
"dd-MM-yyyy",
|
|
).format(pickedDate);
|
|
|
|
if (_selectedCheckInDate != null &&
|
|
_selectedCheckOutDate!.isBefore(_selectedCheckInDate!)) {
|
|
errorMessages["valid_upto${entry['local_id']}"] =
|
|
"Invalid Date Range";
|
|
} else {
|
|
errorMessages.remove("valid_upto${entry['local_id']}");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
"Valid To",
|
|
style: GoogleFonts.poppins(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF575A74),
|
|
),
|
|
),
|
|
SizedBox(height: 5),
|
|
CustomTextFieldUserTravellerWrapper(
|
|
width:
|
|
widget.isDesktop
|
|
? MediaQuery.of(context).size.width * 0.15
|
|
: null,
|
|
isFocused: false,
|
|
isDesktop: widget.isDesktop,
|
|
child: SizedBox(
|
|
height: 40,
|
|
child: GestureDetector(
|
|
onTap: () async {
|
|
errorMessages.remove("valid_upto${entry['local_id']}");
|
|
await _selectValidUpToDate(context);
|
|
},
|
|
child: AbsorbPointer(
|
|
child: TextField(
|
|
// focusNode: _dateFocusNode,
|
|
// controller: _dateController,
|
|
controller: entry["controller_valid_upto"],
|
|
readOnly: true,
|
|
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["valid_upto${entry['local_id']}"] != null) ...[
|
|
SizedBox(height: 5), // Space before error message
|
|
Text(
|
|
errorMessages["valid_upto${entry['local_id']}"]!,
|
|
style: GoogleFonts.poppins(color: Colors.red, fontSize: 10),
|
|
maxLines: 2, // Allow it to wrap onto two lines
|
|
overflow:
|
|
TextOverflow.ellipsis, // Add ellipsis if it still overflows
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
// Widget buildVisaValidFrom(Map<String, dynamic> entry) {
|
|
// DateTime? _selectedCheckOutDate;
|
|
// TimeOfDay? _selectedCheckOutTime;
|
|
//
|
|
// Future<void> _selectValidFromDate(BuildContext context) async {
|
|
// DateTime now = DateTime.now();
|
|
// DateTime today = DateTime(now.year, now.month, now.day);
|
|
//
|
|
// // Parse date from notifier if available, else use today
|
|
// DateTime initialDate = today;
|
|
//
|
|
// // Use previously selected date if valid
|
|
// if (_selectedCheckOutDate != null &&
|
|
// _selectedCheckOutDate!.isAfter(today)) {
|
|
// initialDate = _selectedCheckOutDate!;
|
|
// }
|
|
//
|
|
// final pickedDate = await showDatePicker(
|
|
// context: context,
|
|
// // initialDate: initialDate,
|
|
// // firstDate: initialDate,
|
|
// firstDate: DateTime(1900),
|
|
// lastDate: DateTime(2100),
|
|
// initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
// );
|
|
//
|
|
// if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
|
// setState(() {
|
|
// _selectedCheckOutDate = pickedDate;
|
|
// // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
|
|
//
|
|
// entry["controller_valid_from"].text = DateFormat(
|
|
// 'dd-MM-yyyy',
|
|
// ).format(pickedDate);
|
|
// });
|
|
// }
|
|
// }
|
|
//
|
|
// return Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// Text(
|
|
// "Valid From",
|
|
// style: GoogleFonts.poppins(
|
|
// fontSize: 12,
|
|
// fontWeight: FontWeight.w600,
|
|
// color: Color(0xFF575A74),
|
|
// ),
|
|
// ),
|
|
// SizedBox(height: 5),
|
|
// CustomTextFieldUserTravellerWrapper(
|
|
// width:
|
|
// widget.isDesktop
|
|
// ? MediaQuery.of(context).size.width * 0.15
|
|
// : null,
|
|
// isFocused: false,
|
|
// isDesktop: widget.isDesktop,
|
|
// child: SizedBox(
|
|
// height: 40,
|
|
// child: GestureDetector(
|
|
// onTap: () async {
|
|
// await _selectValidFromDate(context);
|
|
// },
|
|
// child: AbsorbPointer(
|
|
// child: TextField(
|
|
// // focusNode: _dateFocusNode,
|
|
// controller: entry["controller_valid_from"],
|
|
//
|
|
// 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,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// );
|
|
// }
|
|
//
|
|
// Widget buildVisaValidUpTo(Map<String, dynamic> entry) {
|
|
// DateTime? _selectedCheckOutDate;
|
|
// TimeOfDay? _selectedCheckOutTime;
|
|
//
|
|
// Future<void> _selectValidUpToDate(BuildContext context) async {
|
|
// DateTime now = DateTime.now();
|
|
// DateTime today = DateTime(now.year, now.month, now.day);
|
|
// print("buildVisaValidUpTo ");
|
|
// print("buildVisaValidUpTo - ${entry["controller_valid_from"].text}");
|
|
//
|
|
// // Parse date from notifier if available, else use today
|
|
// DateTime initialDate = today;
|
|
//
|
|
// // Use previously selected date if valid
|
|
// // if (_selectedCheckOutDate != null &&
|
|
// // _selectedCheckOutDate!.isAfter(today)) {
|
|
// // initialDate = _selectedCheckOutDate!;
|
|
// // }
|
|
//
|
|
// DateTime? validFromDate;
|
|
//
|
|
// try {
|
|
// String fromDateText = entry["controller_valid_from"].text.trim();
|
|
// print("Valid From Text: $fromDateText");
|
|
// if (fromDateText.isNotEmpty) {
|
|
// validFromDate = DateFormat('dd-MM-yyyy').parseStrict(fromDateText);
|
|
// print("Parsed Valid From: $validFromDate");
|
|
// }
|
|
// } catch (e) {
|
|
// print("Error parsing valid from date: $e");
|
|
// }
|
|
//
|
|
// // Use max(today, validFromDate) as firstDate
|
|
// // DateTime firstDate = today;
|
|
// DateTime firstDate = validFromDate ?? today;
|
|
// if (validFromDate != null && validFromDate.isAfter(today)) {
|
|
// firstDate = validFromDate;
|
|
// }
|
|
// final pickedDate = await showDatePicker(
|
|
// context: context,
|
|
// // initialDate: initialDate,
|
|
// // firstDate: initialDate,
|
|
// // firstDate: DateTime(1900),
|
|
// initialDate: firstDate,
|
|
// firstDate: firstDate,
|
|
// lastDate: DateTime(2100),
|
|
// initialEntryMode: DatePickerEntryMode.calendarOnly,
|
|
// );
|
|
//
|
|
// if (pickedDate != null && pickedDate != _selectedCheckOutDate) {
|
|
// setState(() {
|
|
// _selectedCheckOutDate = pickedDate;
|
|
// // _dateController.text = DateFormat('yyyy-MM-dd').format(pickedDate);
|
|
//
|
|
// entry["controller_valid_upto"].text = DateFormat(
|
|
// 'dd-MM-yyyy',
|
|
// ).format(pickedDate);
|
|
// });
|
|
// }
|
|
// }
|
|
//
|
|
// return Column(
|
|
// crossAxisAlignment: CrossAxisAlignment.start,
|
|
// children: [
|
|
// Text(
|
|
// "Valid To",
|
|
// style: GoogleFonts.poppins(
|
|
// fontSize: 12,
|
|
// fontWeight: FontWeight.w600,
|
|
// color: Color(0xFF575A74),
|
|
// ),
|
|
// ),
|
|
// SizedBox(height: 5),
|
|
// CustomTextFieldUserTravellerWrapper(
|
|
// width:
|
|
// widget.isDesktop
|
|
// ? MediaQuery.of(context).size.width * 0.15
|
|
// : null,
|
|
// isFocused: false,
|
|
// isDesktop: widget.isDesktop,
|
|
// child: SizedBox(
|
|
// height: 40,
|
|
// child: GestureDetector(
|
|
// onTap: () async {
|
|
// await _selectValidUpToDate(context);
|
|
// },
|
|
// child: AbsorbPointer(
|
|
// child: TextField(
|
|
// // focusNode: _dateFocusNode,
|
|
// // controller: _dateController,
|
|
// controller: entry["controller_valid_upto"],
|
|
// 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,
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ),
|
|
// ],
|
|
// );
|
|
// }
|
|
|
|
Widget IconForVisa() {
|
|
return Container(
|
|
height: 60, // must define height
|
|
alignment: Alignment.bottomRight,
|
|
child: IconButton(
|
|
icon: Icon(Icons.add_circle, color: Color(0xFF114D8B), size: 30),
|
|
onPressed: () => addVisaEntry(),
|
|
tooltip: "Add Visa Details",
|
|
),
|
|
);
|
|
}
|
|
}
|