import 'package:dropdown_search/dropdown_search.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:intl/intl.dart'; import 'package:responsive_builder/responsive_builder.dart'; import '../../utils/auth_utils.dart'; import '../../widgets/custom_text_field.dart'; import '../../widgets/custom_text_itnerary_sub.dart'; class InsuranceScreen extends StatefulWidget { final List> flightData; final Map? apiData; final Function(String, bool) onClose; final Function(Map) onSaveInsurance; final Map? selectedItem; final String? loginUser; InsuranceScreen({ required this.onClose, required this.apiData, required this.onSaveInsurance, required this.selectedItem, required this.loginUser, required this.flightData, }); @override _InsuranceScreenState createState() => _InsuranceScreenState(); } class _InsuranceScreenState extends State { final GlobalKey _formKey = GlobalKey(); Map selectedValues = {}; late ValueNotifier flightFirstTripDateNotifier; late ValueNotifier flightLastTripDateNotifier; final FocusNode _tripTypeFocusNode = FocusNode(); final FocusNode _hotelNameFocusNode = FocusNode(); final FocusNode _startDateFocusNode = FocusNode(); final FocusNode _endDateFocusNode = FocusNode(); final FocusNode _commentsFocusNode = FocusNode(); final FocusNode _nomineeFocusNode = FocusNode(); late TextEditingController _tripTypeController = TextEditingController(); late TextEditingController _startDateController = TextEditingController(); late TextEditingController _dateOfBirthController = TextEditingController(); late TextEditingController _endDateController = TextEditingController(); late TextEditingController _insuranceCommentsController = TextEditingController(); late TextEditingController _nomineeController = TextEditingController(); late TextEditingController _nomineerelationController = TextEditingController(); Color layoutColor = Colors.grey; bool _isHotelNameFocused = false; bool _startDateFocus = false; bool _endDateFocus = false; bool _commentsFocus = false; bool _nomineeFocus = false; String? selectedTripType; String? selectedInsuranceType; Map errorMessages = {}; Map focusNodes = {}; Map focusStates = {}; List dataHeader = [ "type_of_insurance", "start_date", "end_date", "comments", "nominee_name", "nominee_dob", "nominee_relationship", ]; Map get InsuranceData { Map data = { "type_of_insurance": selectedInsuranceType, "start_date": _startDateController.text, "end_date": _endDateController.text, "comments": _insuranceCommentsController.text, "nominee_name": _nomineeController.text, "nominee_dob": _dateOfBirthController.text, "nominee_relationship": _nomineerelationController.text, "created_by": widget.loginUser, "updated_by": widget.loginUser, }; if (widget.selectedItem != null) { if (widget.selectedItem?["indx"] != null && widget.selectedItem?["indx"] != 0) { data["indx"] = widget.selectedItem!["indx"]; } else if (widget.selectedItem?["insurance_id"] != null && widget.selectedItem?["insurance_id"] != 0) { data["insurance_id"] = widget.selectedItem!["insurance_id"]; } } return data; } @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { loadInitialData(); }); focusNodes.clear(); focusStates.clear(); // Initialize fields dynamically for (var field in dataHeader) { 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; }); }); } print("Focus Nodes Keys: ${focusNodes.keys.toList()}"); print("Focus States Keys: ${focusStates.keys.toList()}"); _insuranceCommentsController = TextEditingController( text: widget.selectedItem?["comments"] ?? "", ); _startDateController = TextEditingController( text: widget.selectedItem?["start_date"] ?? "", ); _endDateController = TextEditingController( text: widget.selectedItem?['end_date'] ?? "", ); _dateOfBirthController = TextEditingController( text: widget.selectedItem?['nominee_dob'] ?? "", ); _nomineerelationController = TextEditingController( text: widget.selectedItem?['nominee_relationship'] ?? "", ); _nomineeController = TextEditingController( text: widget.selectedItem?['nominee_name'] ?? "", ); // Set the selected value if available if (widget.selectedItem != null && widget.selectedItem!["type_of_insurance"] != null) { selectedInsuranceType = widget.selectedItem!["type_of_insurance"].toString(); } flightFirstTripDateNotifier = ValueNotifier(null); flightLastTripDateNotifier = ValueNotifier(null); WidgetsBinding.instance.addPostFrameCallback((_) { final result = getFlightTripDateRange(widget.flightData); flightFirstTripDateNotifier.value = result['firstTripDate']; flightLastTripDateNotifier.value = result['lastTripDate']; // ✅ Only set controller after value is updated // final parsedDate = // DateTime.tryParse(flightFirstTripDateNotifier.value ?? ''); final parsedDate = DateFormat( "dd-MM-yyyy", ).parse(flightFirstTripDateNotifier.value ?? ''); if (parsedDate != null) { _startDateController.text = DateFormat('dd-MM-yyyy').format(parsedDate); } final parsedEndDate = DateFormat( "dd-MM-yyyy", ).parse(flightLastTripDateNotifier.value ?? ''); if (parsedEndDate != null) { _endDateController.text = DateFormat( 'dd-MM-yyyy', ).format(parsedEndDate); } print("parsedDate $parsedDate"); print("parsedEndDate $parsedEndDate"); }); } void loadInitialData() async { String? layoutString = await getLayoutColor(); setState(() { layoutColor = layoutString != null ? Color(int.parse(layoutString)) : Colors.redAccent; }); } Map getFlightTripDateRange( List> flightData, ) { final allTrips = flightData .expand((flight) => flight['trips'] ?? []) .whereType>() .toList(); if (allTrips.isEmpty) { return {'firstTripDate': null, 'lastTripDate': null}; } allTrips.sort((a, b) { final aDate = DateTime.tryParse(a['date'] ?? '') ?? DateTime(1900); final bDate = DateTime.tryParse(b['date'] ?? '') ?? DateTime(1900); return aDate.compareTo(bDate); }); final firstTrip = allTrips.first; final lastTrip = allTrips.last; return { 'firstTripDate': firstTrip['date'], 'lastTripDate': lastTrip['date'], }; } void _addFocusListener(FocusNode node, Function(bool) updateState) { node.addListener(() { setState(() { updateState(node.hasFocus); }); }); } bool isValidData(Map data) { errorMessages.clear(); // Reset errors // Required fields that must not be empty List requiredFields = [ "type_of_insurance", "start_date", "end_date", "nominee_name", "nominee_dob", "nominee_relationship", ]; // Check validation for each field for (String field in requiredFields) { if (data[field] == null || data[field].toString().trim().isEmpty) { errorMessages[field] = "Required"; } } final format = DateFormat("dd-MM-yyyy"); // Additional validation: checkout_date >= checkin_date final checkIn = data["start_date"]; final checkOut = data["end_date"]; if (checkIn != null && checkOut != null && checkIn.toString().isNotEmpty && checkOut.toString().isNotEmpty) { try { final checkInDate = format.parse(checkIn); final checkOutDate = format.parse(checkOut); if (checkOutDate.isBefore(checkInDate)) { errorMessages["end_date"] = "EndDate date cannot be before StartDate"; } } catch (e) { errorMessages["end_date"] = "Invalid date format"; } } return errorMessages.isEmpty; // Valid if there are no errors } void handleSave() { print("Handle Save InsuranceData $InsuranceData"); Map data = InsuranceData; if (!isValidData(data)) { print("Validation Failed: Required fields are missing."); setState(() {}); return; // Stop execution if validation fails } else { widget.onSaveInsurance(InsuranceData); } widget.onClose("Insurance", false); // widget.onClose(false); // Close screen after saving } DateTime? _parseDate(String date) { try { return DateFormat("dd-MM-yyyy").parse(date); // Change format if needed } catch (e) { return null; } } @override void dispose() { // Dispose all dynamically created FocusNodes for (var node in focusNodes.values) { node.dispose(); } _tripTypeFocusNode.dispose(); _tripTypeController.dispose(); _startDateController.dispose(); _endDateController.dispose(); _insuranceCommentsController.dispose(); _nomineeController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return ResponsiveBuilder( builder: (context, sizingInfo) { bool isMobile = sizingInfo.isMobile; bool isDesktop = sizingInfo.deviceScreenType == DeviceScreenType.desktop; return Container( // color: Color(0xFFF4F4FB), child: Form( key: _formKey, child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Padding( padding: const EdgeInsets.only(top: 30.0), child: Center( child: Column(children: _buildAccomadtionForm(isDesktop)), ), ), ], ), ), ), ); }, ); } List _buildAccomadtionForm(bool isDesktop) { List buildResponsiveRow(List children) { return [ isDesktop ? Row(children: children) : Column(children: children), SizedBox(height: 10), ]; } List> rowBuilders = [ // _builClassType(isDesktop), _buildSecondRow(isDesktop), _buildSecondSubRow(isDesktop), ]; return [ // ...buildResponsiveRow(_buildFirstRow(isDesktop)), // Iterate over rowBuilders and wrap each in a responsive container ...rowBuilders.expand((row) => buildResponsiveRow(row)), ...buildResponsiveRow(_buildThirdRow(isDesktop)), // Actions row remains a Row ]; } // List _buildFirstRow(isDesktop) { // return [ // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text( // "Insurance Type", // not in use // style: GoogleFonts.poppins( // fontSize: 12, // fontWeight: FontWeight.w500, // color: Color(0xFF575A74), // ), // ), // SizedBox(height: 5), // isDesktop // ? Row(children: _buildTripType(isDesktop)) // : Column(children: _buildTripType(isDesktop)), // ], // ), // if (isDesktop) Spacer() else SizedBox(height: 8), // ]; // } List _buildTripType(bool isDesktop) { List purposeList = widget.apiData?['insurance_type_of_insurance'] ?? []; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; List> dropdownItems = purposeList .map( (item) => DropdownMenuItem( value: item['dropdown_key'], child: Text(item['dropdown_value']), ), ) .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text( "No options available", style: TextStyle(color: Colors.grey), ), ), ); } // Default selected value selectedInsuranceType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; return [ CustomTextFieldWrapper( isFocused: _isHotelNameFocused, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: DropdownButtonFormField( focusNode: _tripTypeFocusNode, // Assign the correct focus node value: selectedInsuranceType, style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), decoration: InputDecoration( border: InputBorder.none, contentPadding: EdgeInsets.symmetric( horizontal: 10, ), // Proper padding ), onChanged: purposeList.isNotEmpty ? (newValue) { setState(() { selectedInsuranceType = newValue; if (selectedInsuranceType!.isNotEmpty) { errorMessages.remove("type_of_insurance"); } }); print(selectedInsuranceType); } : null, items: dropdownItems, ), ), ), ]; } List _buildInsuranceTypeDropdown(bool isDesktop) { // List purposeList = widget.apiData?['insurance_type_of_insurance'] ?? []; List> purposeList = (widget.apiData?['insurance_type_of_insurance'] as List?) ?.map((e) => Map.from(e as Map)) .toList() ?? []; // Find selected item object based on key Map? selectedItem = purposeList.firstWhere( (item) => item['dropdown_key'] == selectedInsuranceType, orElse: () => {}, ); return [ CustomTextFieldWrapper( isFocused: false, isDesktop: isDesktop, child: SizedBox( height: 35, width: double.infinity, child: DropdownSearch>( popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 10), menuProps: const MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) { return Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 8, ), child: Text( item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, // 👈 Smaller font size color: Colors.black, ), ), ); }, ), dropdownDecoratorProps: const DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( border: InputBorder.none, // No underline contentPadding: EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( "Select Insurance Type", style: GoogleFonts.poppins(color: Colors.grey, fontSize: 13), ); } return Text( selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), ); }, selectedItem: selectedItem!.isNotEmpty ? selectedItem : null, itemAsString: (item) => item['dropdown_value'] ?? '', onChanged: purposeList.isNotEmpty ? (Map? newItem) { if (newItem != null) { setState(() { selectedInsuranceType = newItem['dropdown_key']; errorMessages.remove("type_of_insurance"); }); print( "Selected Insurance Type: $selectedInsuranceType", ); } } : null, items: purposeList, ), ), ), ]; } List _buildSecondRow(bool isDesktop) { List purposeList = widget.apiData?['insurance_type_of_insurance'] ?? []; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; List> dropdownItems = purposeList .map( (item) => DropdownMenuItem( value: item['dropdown_key'], child: Text(item['dropdown_value']), ), ) .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text( "No options available", style: TextStyle(color: Colors.grey), ), ), ); } // Default selected value selectedInsuranceType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; // ---------------------------------------------------------------- DateTime? _selectedCheckOutDate; TimeOfDay? _selectedCheckOutTime; Future _selectCheckOutDate(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; if (flightFirstTripDateNotifier.value != null) { try { initialDate = DateTime.parse(flightFirstTripDateNotifier.value!); } catch (e) { initialDate = today; } } else { 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, lastDate: DateTime(2100), initialEntryMode: DatePickerEntryMode.calendarOnly, ); // DateTime? pickedDate = await showDatePicker( // context: context, // initialDate: _selectedCheckOutDate != null && // _selectedCheckOutDate!.isAfter(today) // ? _selectedCheckOutDate! // : today, // firstDate: today, // lastDate: DateTime(2100), // ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; _startDateController.text = DateFormat( 'dd-MM-yyyy', ).format(pickedDate); }); } } Future _selectEndCheckOutDate(BuildContext context) async { DateTime now = DateTime.now(); DateTime today = DateTime(now.year, now.month, now.day); // DateTime? checkInDate; // try { // checkInDate = DateTime.parse(_startDateController.text); // } catch (e) { // checkInDate = today; // } // // Ensure at least today is used // DateTime firstDate = checkInDate.isAfter(today) ? checkInDate : today; // DateTime initialDate = _selectedCheckOutDate != null && // _selectedCheckOutDate!.isAfter(firstDate) // ? _selectedCheckOutDate! // : firstDate; // DateTime firstDate = checkInDate; // DateTime initialDate = // _selectedCheckOutDate != null && // _selectedCheckOutDate!.isAfter(firstDate) // ? _selectedCheckOutDate! // : firstDate; DateTime? validFromDate; try { String fromDateText = _startDateController.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, ); // final pickedDate = await showDatePicker( // context: context, // // initialDate: initialDate, // initialDate: initialDate, // firstDate: initialDate, // lastDate: DateTime(2100), // initialEntryMode: DatePickerEntryMode.calendarOnly, // ); // DateTime? pickedDate = await showDatePicker( // context: context, // initialDate: _selectedCheckOutDate != null && // _selectedCheckOutDate!.isAfter(today) // ? _selectedCheckOutDate! // : today, // firstDate: today, // lastDate: DateTime(2100), // ); if (pickedDate != null && pickedDate != _selectedCheckOutDate) { setState(() { _selectedCheckOutDate = pickedDate; _endDateController.text = DateFormat('dd-MM-yyyy').format(pickedDate); }); } } return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Insurance Type *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: const Color(0xFF575A74), ), ), const SizedBox(height: 5), // ▼ Dropdown Wrapper ▼ // CustomTextFieldWrapper( // isFocused: _isHotelNameFocused, // isDesktop: isDesktop, // width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, // child: SizedBox( // height: 35, // width: double.infinity, // child: DropdownButtonFormField( // focusNode: _tripTypeFocusNode, // value: selectedInsuranceType, // style: GoogleFonts.poppins(fontSize: 12, color: Colors.black), // decoration: const InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric( // horizontal: 10, // vertical: 5, // ), // ), // hint: Text( // "Select ", // style: GoogleFonts.poppins(color: Colors.grey, fontSize: 13), // ), // items: // purposeList.map>((item) { // return DropdownMenuItem( // value: item['dropdown_key'], // child: Text( // item['dropdown_value'] ?? '', // style: GoogleFonts.poppins(fontSize: 12), // ), // ); // }).toList(), // onChanged: // purposeList.isNotEmpty // ? (String? newValue) { // setState(() { // selectedInsuranceType = newValue; // if ((selectedInsuranceType ?? '').isNotEmpty) { // errorMessages.remove("type_of_insurance"); // } // }); // print( // "Selected Insurance Type: $selectedInsuranceType", // ); // } // : null, // ), // ), // ), // ▼ Dropdown Wrapper ▼ CustomTextFieldWrapper( // isFocused: _isHotelNameFocused, isFocused: false, padding: const EdgeInsets.symmetric(horizontal: 0), isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, child: SizedBox( height: 40, width: double.infinity, child: DropdownSearch>( items: purposeList.cast>(), selectedItem: purposeList.firstWhere( (item) => item['dropdown_key'] == selectedInsuranceType, orElse: () => {}, ), itemAsString: (item) => item['dropdown_value'] ?? '', popupProps: PopupProps.menu( showSearchBox: false, fit: FlexFit.loose, menuProps: const MenuProps(backgroundColor: Colors.white), itemBuilder: (context, item, isSelected) { return Padding( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), child: Text( item['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ), ); }, ), dropdownDecoratorProps: DropDownDecoratorProps( dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, // No underline border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: (focusStates["type_of_insuranceFocusNode"] ?? false) ? layoutColor! : Colors.white, width: 0.5, ), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: (focusStates["type_of_insuranceFocusNode"] ?? false) ? layoutColor! : Colors.white, // : const Color(0xFFD6D5E6), width: 0.5, // const Color(0xFFD6D5E6), ), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: layoutColor!, width: 1), ), contentPadding: const EdgeInsets.symmetric( horizontal: 10, vertical: 5, ), ), // dropdownSearchDecoration: InputDecoration( // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric( // horizontal: 10, // vertical: 10, // ), // ), ), dropdownBuilder: (context, selectedItem) { if (selectedItem == null || selectedItem.isEmpty) { return Text( "Select ", style: GoogleFonts.poppins( color: Colors.grey, fontSize: 13, ), ); } return Text( selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), ); }, onChanged: purposeList.isNotEmpty ? (Map? newValue) { setState(() { selectedInsuranceType = newValue?['dropdown_key']; print( "selected Insurance Type: ${selectedInsuranceType}", ); }); } : null, ), ), ), ], ), // if (isDesktop) Spacer() else SizedBox(height: 8), // Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text( // "Nominee", // style: GoogleFonts.poppins( // fontSize: 12, // fontWeight: FontWeight.w600, // color: Color(0xFF575A74), // ), // ), // SizedBox(height: 5), // CustomTextFieldWrapper( // isFocused: _nomineeFocus, // isDesktop: isDesktop, // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, // // width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, // child: SizedBox( // height: 40, // child: TextField( // focusNode: _nomineeFocusNode, // controller: _nomineeController, // style: TextStyle(fontSize: 12), // decoration: InputDecoration( // labelText: "Nominee", // labelStyle: TextStyle(fontSize: 12, color: Colors.grey), // floatingLabelBehavior: FloatingLabelBehavior.never, // border: InputBorder.none, // contentPadding: EdgeInsets.symmetric(vertical: 16), // // contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1), // ), // ), // ), // ), // ], // ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Start Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( // isFocused: _startDateFocus, isFocused: focusStates["start_dateFocused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { focusNodes["start_dateFocusNode"]?.requestFocus(); await _selectCheckOutDate(context); if (_startDateController.text.isNotEmpty) { setState(() { errorMessages.remove( "start_date", ); // Removes the key completely }); } }, child: AbsorbPointer( child: TextField( // focusNode: _startDateFocusNode, focusNode: focusNodes["start_dateFocusNode"], controller: _startDateController, readOnly: true, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select ", 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["start_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "End Date *", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["end_dateFocused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { focusNodes["end_dateFocusNode"]?.requestFocus(); await _selectEndCheckOutDate(context); if (_endDateController.text.isNotEmpty) { DateTime? startDate = _parseDate(_startDateController.text); DateTime? endDate = _parseDate(_endDateController.text); if (startDate != null && endDate != null && endDate.isBefore(startDate)) { setState(() { errorMessages["end_date"] = "End date cannot be earlier than start date"; }); } else { setState(() { errorMessages.remove("end_date"); }); } } }, child: AbsorbPointer( child: TextField( // focusNode: _endDateFocusNode, focusNode: focusNodes["end_dateFocusNode"], controller: _endDateController, readOnly: true, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select ", 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["end_date"] != null) ...[ SizedBox(height: 5), // Space before error message Text( errorMessages["end_date"]!, style: const TextStyle(color: Colors.red, fontSize: 12), maxLines: 2, // Allow it to wrap onto two lines overflow: TextOverflow.ellipsis, // Add ellipsis if it still overflows ), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), ]; } List _buildSecondSubRow(bool isDesktop) { List purposeList = widget.apiData?['insurance_type_of_insurance'] ?? []; // selectedInsuranceType = purposeList.isNotEmpty ? purposeList.first['dropdown_value'] : null; List> dropdownItems = purposeList .map( (item) => DropdownMenuItem( value: item['dropdown_key'], child: Text(item['dropdown_value']), ), ) .toList(); if (dropdownItems.isEmpty) { dropdownItems.add( DropdownMenuItem( value: null, child: Text( "No options available", style: TextStyle(color: Colors.grey), ), ), ); } // Default selected value selectedInsuranceType ??= dropdownItems.isNotEmpty ? dropdownItems.first.value : null; // ---------------------------------------------------------------- DateTime? _selectedCheckDOBDate; Future _selectCheckDOBOutDate(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; initialDate = today; final pickedDate = await showDatePicker( context: context, initialDate: initialDate, firstDate: DateTime(1900), lastDate: today, initialEntryMode: DatePickerEntryMode.calendarOnly, ); // DateTime? pickedDate = await showDatePicker( // context: context, // initialDate: _selectedCheckOutDate != null && // _selectedCheckOutDate!.isAfter(today) // ? _selectedCheckOutDate! // : today, // firstDate: today, // lastDate: DateTime(2100), // ); if (pickedDate != null && pickedDate != _selectedCheckDOBDate) { setState(() { _selectedCheckDOBDate = pickedDate; _dateOfBirthController.text = DateFormat( 'dd-MM-yyyy', ).format(pickedDate); }); } } return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Nominee*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w600, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( // isFocused: _nomineeFocus, isFocused: focusStates["nominee_nameFocused"] ?? false, isDesktop: isDesktop, // width: isDesktop ? MediaQuery.of(context).size.width * 0.15 : null, width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, child: SizedBox( height: 40, child: TextField( focusNode: focusNodes["nominee_nameFocusNode"], inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')), ], // focusNode: _nomineeFocusNode, controller: _nomineeController, style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Nominee", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), // contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1), ), ), ), ), if (errorMessages["nominee_name"] != null) ...[ SizedBox(height: 5), // Space before error message Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Date of Birth*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["nominee_dobFocused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null, child: SizedBox( height: 40, child: GestureDetector( onTap: () async { focusNodes["nominee_dobFocusNode"]?.requestFocus(); await _selectCheckDOBOutDate(context); // await _selectDateOfBirth(context); if (_dateOfBirthController.text.isNotEmpty) { setState(() { errorMessages.remove( "nominee_dob", ); // Removes the key completely }); } }, child: AbsorbPointer( child: TextField( focusNode: focusNodes["nominee_dobFocusNode"], controller: _dateOfBirthController, readOnly: true, style: const TextStyle(fontSize: 12), decoration: const InputDecoration( labelText: "Select ", 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["nominee_dob"] != null) ...[ SizedBox(height: 5), // Space before error message Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Relationship*", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), // CustomTextFieldWrapper( isFocused: focusStates["nominee_relationshipFocused"] ?? false, isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.16 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, // width: isDesktop ? MediaQuery.of(context).size.width * 0.34 : null, child: SizedBox( height: 40, child: TextField( focusNode: focusNodes["nominee_relationshipFocusNode"], controller: _nomineerelationController, style: TextStyle(fontSize: 12), inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')), ], decoration: InputDecoration( labelText: "Nominee Relationship", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), // contentPadding: EdgeInsets.symmetric(horizontal: 1,vertical: 1), ), ), ), ), if (errorMessages["nominee_relationship"] != null) ...[ SizedBox(height: 5), // Space before error message Text("Required", style: TextStyle(color: Colors.red, fontSize: 12)), ], ], ), if (isDesktop) Spacer() else SizedBox(height: 8), ]; } List _buildThirdRow(bool isDesktop) { return [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Comments", style: GoogleFonts.poppins( fontSize: 12, fontWeight: FontWeight.w500, color: Color(0xFF575A74), ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: focusStates["commentsFocused"] ?? false, // Dropdown doesn't use focus isDesktop: isDesktop, width: isDesktop ? MediaQuery.of(context).size.width * 0.31 : null, // MediaQuery.of(context).size.width * 0.66, child: SizedBox( height: 40, child: TextField( // focusNode: _commentsFocusNode, focusNode: focusNodes["commentsFocusNode"], controller: _insuranceCommentsController, inputFormatters: [ FilteringTextInputFormatter.allow(RegExp(r'[a-zA-Z0-9 _-]')), ], style: TextStyle(fontSize: 12), decoration: InputDecoration( labelText: "Comments", labelStyle: TextStyle(fontSize: 12, color: Colors.grey), floatingLabelBehavior: FloatingLabelBehavior.never, border: InputBorder.none, contentPadding: EdgeInsets.symmetric(vertical: 16), ), ), ), ), ], ), if (isDesktop) Spacer(), SizedBox(height: 5), Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.end, children: _handleAction(isDesktop), ), ], ), ]; } List _handleAction(bool isDesktop) { return [ // Close Button ElevatedButton( onPressed: () { widget.onClose("Insurance", false); // Close the dialog or screen }, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey[400], // Light grey color shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Close", style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), SizedBox(width: 10), // Space between buttons // Save Changes Button ElevatedButton( onPressed: () { handleSave(); }, style: ElevatedButton.styleFrom( backgroundColor: Color(0xFF114D8B), // Primary color for save shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12), ), child: Text( "Save Changes", style: GoogleFonts.poppins(color: Colors.white, fontSize: 12), ), ), ]; } }