import 'dart:convert'; import 'package:dropdown_search/dropdown_search.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:http/http.dart' as http; import 'package:http/http.dart' as ref; import 'package:toastification/toastification.dart'; import '../../../../core/config/env.dart'; import '../../../../core/services/api_service.dart'; import '../../../../data/services/auth_service.dart'; import '../../../../data/utils/toastNotification.dart'; import '../../../../data/utils/validators.dart'; import '../../../layouts/responsive_layout.dart'; import '../../../themes/indicators/customizd_file_upload.dart'; import '../../../themes/indicators/input_field_decoration.dart'; import '../../../themes/indicators/search_field_theme.dart'; import '../../../themes/indicators/text_field_theme.dart'; import '../../../widgets/policy_search_results_list.dart'; // 🔹 Custom Dialog Widget class AddDialog extends StatefulWidget { final String title; final dynamic userId; final dynamic managerId; final dynamic role; final void Function(String value) onSubmit; // final dynamic policyNumber; const AddDialog({ super.key, required this.title, required this.onSubmit, required this.userId, required this.managerId, required this.role, // this.policyNumber, }); @override State createState() => _AddDialogState(); } class _AddDialogState extends State { late ApiService apiService; String? _token; bool isLoading = false; late TextEditingController controller; Map controllers = {}; String? selectedClaimFileNames; String? passportFileUrlFromApi; String? selectedClaimId; PlatformFile? docUploadedFile; String? selectedFileNames; String? passportEndorsFileUrlFromApi; String? selectedEndorsId; PlatformFile? docUploadedEndorsFile; final _formKeyClaims = GlobalKey(); final _formKeyEndrosment = GlobalKey(); final GlobalKey>> dropDownKey = GlobalKey>>(); final GlobalKey>> dropDownKeyEndorsement = GlobalKey>>(); List tabHeader = ['policyNum', 'claimsDesc', 'remarks']; final TextEditingController _vehicleSearchController = TextEditingController(); final TextEditingController _policySearchController = TextEditingController(); final TextEditingController _searchStaffController = TextEditingController(); List> getClaimsTypeData = []; List> filteredClaimsData = []; List> getEndrosmentType = []; List> filteredEndrosmentData = []; String? selectedClaimsType; String? selectedEndorsement; String? selectedEndrosementPolicyFrom; dynamic roleId; List> getPolicyData = []; List> originalData = []; List> filteredData = []; List> getPolicyListData = []; List> originalPolicyData = []; List> filteredPolicyData = []; List> getVehicleListData = []; List> originalVehicleData = []; List> filteredVehicleData = []; final List> policyFromOptions = [ {"policy_from": "Internal"}, {"policy_from": "External"}, ]; Map claimsDetails() { final data = { "policy_number": controllers["policyNum"]?.text, "claim_description": controllers["claimsDesc"]?.text, "claim_type": selectedClaimsType, // "manager_id": managerId, "created_by": widget.userId, }; return data; } Map endrosmentDetails() { final data = { "policy_number": controllers["policyNum"]?.text, "endorsement_description": controllers["remarks"]?.text, "endorsement_type": selectedEndorsement, "manager_id": widget.managerId, "created_by": widget.userId, "policy_from": selectedEndrosementPolicyFrom, }; return data; } @override void initState() { super.initState(); apiService = ApiService(); for (String field in tabHeader) { controllers[field] = TextEditingController(); } // controllers["policyNum"]?.text = widget.policyNumber; print('userIFF - ${widget.userId}'); print('roleIFF - ${widget.role}'); Future.microtask(() { // final id = ref.read(managerIdProvider); // roleId = ref.read(userRoleProvider); // userId = ref.read(userIdProvider); if (widget.userId != null && widget.managerId != null) { getStaffList(widget.managerId, widget.userId); } }); // 🔹 Init logic here (API calls, token fetch, etc.) _initializeToken(); getClaimsType(); getEnroementType(); } Future getPolicyListSearchData(String val) async { setState(() { isLoading = true; }); try { final response = await apiService.fetchPolicySearch(val); if (response['status'] == 'success' && response['data'] != null) { final dataList = List>.from(response['data']); setState(() { getPolicyListData = dataList; originalPolicyData = dataList; filteredPolicyData = List.from(originalPolicyData); }); } else { setState(() { getPolicyListData = []; originalPolicyData = []; filteredPolicyData = []; }); } } catch (e) { print('Exception occurred: $e'); setState(() { getPolicyListData = []; originalPolicyData = []; filteredPolicyData = []; }); } finally { setState(() { isLoading = false; }); } } Future getVehicleListSearchData(String val) async { setState(() { isLoading = true; }); try { final response = await apiService.fetchVehicleSearch(val); if (response['status'] == 'success') { print('E113 => getStaffListData => ${response['data']}'); setState(() { getVehicleListData = List>.from( response['data'], ); originalVehicleData = getVehicleListData; filteredVehicleData = List.from(originalVehicleData); // print('originalData - $getClaimPolicies'); }); } else { getVehicleListData = []; originalVehicleData = []; } } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoading = false; }); } } Future getPolicyListSearchData1(val) async { setState(() { isLoading = true; }); try { final response = await apiService.fetchPolicySearch(val); if (response['status'] == 'success') { print('E113 => getStaffListData => ${response['data']}'); setState(() { getPolicyListData = List>.from(response['data']); // filteredData = getPolicyData.where((item) { // return (item['agent_name'] ?? '-').toLowerCase() || // (item['reg_no'] ?? '-').toLowerCase; // }).toList(); originalPolicyData = getPolicyListData; filteredPolicyData = List.from(originalPolicyData); // print('originalData - $getClaimPolicies'); }); } else { getPolicyListData = []; originalPolicyData = []; } } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoading = false; }); } } Future getStaffList( dynamic managerId, role, { String fromDate = '', String toDate = '', }) async { print('D68 => Fns called => $managerId | $role'); setState(() { isLoading = true; }); final id = int.parse(managerId); // FORCE BOTH VALUES TO STRING final String managerIdStr = managerId?.toString() ?? ''; final String roleStr = role?.toString() ?? ''; try { final response = await apiService.fetchPolicyDataOnlyList( managerIdStr, roleStr, roleStr, fromDate: controllers['startDate']?.text ?? '', toDate: controllers['endDate']?.text ?? '', selectedStaffId: widget.userId ?? '', ); if (response['status'] == 'success') { final data = response['data']; print('D81 => getStaffListData => ${response['data']}'); final fromDate = response['from_date'] ?? ''; final toDate = response['to_date'] ?? ''; print('FromDate : $fromDate'); print('ToDate : $toDate'); setState(() { controllers['startDate']?.text = fromDate; controllers['endDate']?.text = toDate; if (data is List) { // Already a list of maps getPolicyData = List>.from(data); } else if (data is Map) { // Single object, wrap in a list getPolicyData = [Map.from(data)]; } else { getPolicyData = []; } // getStaffData = List>.from(response['data']); originalData = getPolicyData; filteredData = List.from(originalData); // print('originalData - $getClaimPolicies'); }); } else { getPolicyData = []; originalData = []; } } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoading = false; }); } } void filterData(String query) { print("FilterDAta - $query"); setState(() { filteredData = getPolicyData.where((item) { // final isActiveStatus = item['is_active'] == "1" ? "active" : "inactive"; return (item['agent_name'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || (item['reg_no'] ?? '-').toLowerCase().contains( query.toLowerCase(), ) || (item['insurer_name'] ?? '-').toLowerCase().contains( query.toLowerCase(), ); }).toList(); }); } Future _initializeToken() async { _token = await AuthService.getToken(); print("APISERTOKEN - $_token"); } @override void dispose() { // Dispose all TextEditingControllers for (var controller in controllers.values) { controller.dispose(); } super.dispose(); } Future getClaimsType() async { print('getClaimList called'); setState(() { isLoading = true; }); try { final response = await apiService.fetchMasterDropDown('Claim'); if (response['status'] == 200) { print('getClaimsTypeData - ${response['data']}'); setState(() { getClaimsTypeData = List>.from(response['data']); print('API Data - $getClaimsTypeData'); filteredClaimsData = List.from(getClaimsTypeData); // print('originalData - $filteredClaimsData'); }); } else { getClaimsTypeData = []; filteredClaimsData = []; } } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoading = false; }); } } Future getEnroementType() async { print('Insurers called'); setState(() { isLoading = true; }); try { final response = await apiService.fetchMasterDropDown('Endorsement'); if (response['status'] == 200) { print('getEnroementType - ${response['data']}'); setState(() { getEndrosmentType = List>.from(response['data']); print('API Data - $getEndrosmentType'); filteredEndrosmentData = List.from(getEndrosmentType); // print('originalData - $filteredEndrosmentData'); }); } else { getEndrosmentType = []; filteredEndrosmentData = []; } } catch (e) { print('Exception occurred: $e'); } finally { setState(() { isLoading = false; }); } } void handleDone(val) { print('val - $val'); if (val == 'Claims') { if (!_formKeyClaims.currentState!.validate()) return; setState(() { if (_formKeyClaims.currentState!.validate()) { claimsDetails(); final dataSet = claimsDetails(); print("dataSetAgent - $dataSet"); // print("managerId - $managerId ,userId - $userId "); createUserData(dataSet, val); } else { // isDi sable = false; } }); } else { if (!_formKeyEndrosment.currentState!.validate()) return; setState(() { if (_formKeyEndrosment.currentState!.validate()) { endrosmentDetails(); final dataSet = endrosmentDetails(); print("dataSetAgent - $dataSet"); // print("managerId - $managerId ,userId - $userId "); createUserData(dataSet, val); } else { // isDisable = false; } }); } } Future createUserData(data, val) async { final endpoints = { 'Claims': 'claim/createClaim', 'Endorsement': 'endorsement/createEndorsement', }; final String? path = endpoints[val]; final Uri uri = Uri.parse('${Env.apiUrl}$path'); if (_token == null) { throw Exception('Token not found. Please log in.'); } // Use MultipartRequest (POST only) final request = http.MultipartRequest('POST', uri); request.headers['Authorization'] = 'Bearer $_token'; request.headers['app-signature'] = Env.App_Signature; print("USerDAta - $data"); // userData.forEach((key, value) { // request.fields[key] = value.toString(); // print("✅ Encoded travel_details2: ${request.fields[key]}"); // }); data.forEach((key, value) { // if (key != 'certificate_file_name') { request.fields[key] = value.toString(); print("✅ Encoded $key: ${request.fields[key]}"); // } }); if (val == 'Claims') { // Attach file if selected if (docUploadedFile != null) { try { if (docUploadedFile!.bytes != null) { final multipartFile = http.MultipartFile.fromBytes( 'claim_file_name', docUploadedFile!.bytes!, filename: docUploadedFile!.name, ); request.files.add(multipartFile); } // else if (docUploadedFile!.path != null) { // final multipartFile = await http.MultipartFile.fromPath( // 'certificate_file_name', // docUploadedFile!.path!, // filename: docUploadedFile!.name, // ); // request.files.add(multipartFile); // } print("📎 File attached: ${docUploadedFile!.name}"); } catch (e) { print("❌ Failed to attach file: $e"); } } } else { // Attach file if selected if (docUploadedEndorsFile != null) { try { if (docUploadedEndorsFile!.bytes != null) { final multipartFile = http.MultipartFile.fromBytes( 'endorsement_file_name', docUploadedEndorsFile!.bytes!, filename: docUploadedEndorsFile!.name, ); request.files.add(multipartFile); } // else if (docUploadedEndorsFile!.path != null) { // final multipartFile = await http.MultipartFile.fromPath( // 'endorsement_file_name', // docUploadedEndorsFile!.path!, // filename: docUploadedEndorsFile!.name, // ); // request.files.add(multipartFile); // } print("📎 File attached: ${docUploadedEndorsFile!.name}"); } catch (e) { print("❌ Failed to attach file: $e"); } } } print(" Sending request with fields: ${request.fields}"); try { final streamedResponse = await request.send(); final response = await http.Response.fromStream(streamedResponse); print("Response status: ${response.statusCode}"); print("Response body: ${response.body}"); if (response.statusCode == 200 || response.statusCode == 201) { print("✅ Partner submitted successfully!"); print("Response: ${response.body}"); ToastHelper.showSuccessToast(context, 'Saved Successfully'); Navigator.of(context).pop(); widget.onSubmit("success"); } else if (response.statusCode == 403) { await apiService.clearLocalStorageAndRedirect(); } else { final responseBody = jsonDecode(response.body); dynamic msg = responseBody['data']; print("Failed to submit plan. Status: ${response.statusCode}"); print("Error: ${response.body}"); ToastHelper.showErrorToast(context, 'Failed To Save'); } } catch (e) { print("🔥 Error submitting user: $e"); } } @override Widget build(BuildContext context) { return SelectionArea( child: AlertDialog( backgroundColor: Colors.white, content: Column( mainAxisSize: MainAxisSize.min, children: [ // Header row Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( widget.title, style: GoogleFonts.inter( color: const Color(0xFF374141), fontSize: 18, fontWeight: FontWeight.bold, ), ), GestureDetector( onTap: () => Navigator.pop(context), child: Container( padding: const EdgeInsets.all(5.0), decoration: BoxDecoration( color: const Color(0xFFF1F1F1), borderRadius: BorderRadius.circular(5.0), ), child: Tooltip( message: 'Close', child: const Icon(Icons.close, size: 18), ), ), ), ], ), const SizedBox(height: 16), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( children: [ ThemedSearchField( hintText: 'Search By Vehicle Number', backgroundColor: Color(0xFFFFFFFF), txtHeight: 30, controller: _vehicleSearchController, onChanged: (val) async { if (val.isNotEmpty) { await getVehicleListSearchData(val); } else { setState(() { // filteredVehicleData = List.from(originalVehicleData); filteredVehicleData.clear(); }); } }, txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, ), if (filteredVehicleData.isNotEmpty) SizedBox( width: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, child: PolicySearchResultsList( rows: filteredVehicleData, fromVehicle: true, onSelected: (vehicle) { setState(() { controllers['policyNum']?.text = PolicySearchUtils.policyNumberFromRow(vehicle); _vehicleSearchController.text = PolicySearchUtils.vehicleInputValueOnSelect( vehicle, ); filteredVehicleData.clear(); }); }, ), ), ], ), SizedBox(width: 15), Column( children: [ ThemedSearchField( hintText: 'Search By Policy Number', backgroundColor: Color(0xFFFFFFFF), txtHeight: 30, controller: _policySearchController, onChanged: (val) async { if (val.isNotEmpty) { await getPolicyListSearchData(val); } else { setState(() { // filteredPolicyData = List.from(originalPolicyData); filteredPolicyData.clear(); }); } }, txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, ), // Policy Search Results if (filteredPolicyData.isNotEmpty) Container( width: MediaQuery.of(context).size.width * 0.26, // optional height: 150, decoration: BoxDecoration( color: Colors.white, border: Border.all(color: Colors.blueGrey.shade100), borderRadius: BorderRadius.circular(5), ), child: ListView.builder( itemCount: filteredPolicyData.length, itemBuilder: (context, index) { final policy = filteredPolicyData[index]; return InkWell( onTap: () { setState(() { controllers['policyNum']?.text = policy['policy_number']; filteredPolicyData.clear(); }); }, child: Container( padding: const EdgeInsets.symmetric( vertical: 3, horizontal: 8, ), // color: Colors.amber, child: Text( policy['policy_number'] ?? '-', style: _textStyle1, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), ); // ListTile( // dense: true, // ← reduces row height // contentPadding: EdgeInsets.symmetric( // horizontal: 10, // vertical: 2, // ), // subtitle: Text( // // style: _textStyle1, // ), // onTap: () { // setState(() { // // // populate other fields as needed // }); // }, // ); }, ), ), ], ), ], ), const SizedBox(height: 10), const SizedBox(height: 16), // 🔹 Switch content dynamically widget.title == 'Claims' ? claims(context) : endorsement(context), ], ), actions: [ GestureDetector( onTap: () { handleDone(widget.title); // widget.onSubmit(controller.text.trim()); // Navigator.of(context).pop(); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 25.0, vertical: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8.0), color: const Color(0xFF425B5B), ), child: const Text('Save', style: TextStyle(color: Colors.white)), ), ), ], ) ); } // 🔹 Example: Claims widget Widget claims(BuildContext context) { return Form( key: _formKeyClaims, child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ buildClaimType(context), SizedBox(width: 15), buildPolicyNumber(context), ], ), SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ buildUploadDocumentClaims(context), SizedBox(width: 15), buildClaimsDesc(context), ], ), ], ), ); } // 🔹 Example: Endorsement widget Widget endorsement(BuildContext context) { return Form( key: _formKeyEndrosment, child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ buildEndrosType(context), SizedBox(width: 15), buildEndrosPolicyFrom(context), ], ), SizedBox(height: 10), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ buildUploadDocumentClaims(context), SizedBox(width: 15), buildPolicyNumber(context), ], ), SizedBox(height: 10), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ buildEndrosRemarks(context), SizedBox(width: 15), ], ), ], ), ); } // ------------------------- Claims Part ----------------------------------- Widget buildPolicyNumber(context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Policy Number *", style: _textStyle), SizedBox(height: 10), ThemedFormField( controller: controllers['policyNum']!, backgroundColor: Color(0xFFEDF6F5), readOnly: true, validator: (value) => Validators.requiredField(value, "name"), txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, ), ], ); } Widget buildClaimsDesc(context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Claims Description ", style: _textStyle), SizedBox(height: 10), ThemedFormField( backgroundColor: Color(0xFFEDF6F5), controller: controllers['claimsDesc']!, // txtheight: 45, validator: (value) => Validators.requiredField(value, "claimsDesc"), txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, keyboardType: TextInputType.multiline, ), ], ); } Widget buildClaimType(context) { Map? selectedVehicle = filteredClaimsData.firstWhere( (item) => item['id'].toString() == selectedClaimsType, orElse: () => {}, ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Claims Type *", style: _textStyle), SizedBox(height: 10), Container( color: Colors.white, width: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, // height: 40, child: DropdownSearch>( key: dropDownKey, selectedItem: selectedVehicle.isNotEmpty ? selectedVehicle : null, items: (filter, infiniteScrollProps) { return filteredClaimsData; }, itemAsString: (val) => val['claim_type'].toString(), compareFn: (item, selectedItem) => item['id'] == selectedItem['id'], // ✅ compare by id validator: (val) { if (val == null) { return "Required"; // ✅ error message } return null; }, decoratorProps: DropDownDecoratorProps( decoration: AppInputDecorations.dropdownDecoration( label: "Select Claims Type", ).copyWith( filled: true, fillColor: Color( 0xFFEDF6F5,), // 👈 makes the dropdown input white isDense: true, // 👈 Makes the field compact contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 0), // 👈 Vertical 0 helps center the text ), ), popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 250), menuProps: MenuProps( backgroundColor: Colors.white, // 👈 sets dropdown background to white ), showSearchBox: true, searchFieldProps: TextFieldProps( decoration: InputDecoration( filled: true, fillColor: Colors.white, hintText: "Search Claims Type...", enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.white, ), // 👈 Normal border ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.white, width: 1.5, ), // 👈 Focused border ), ), ), // constraints: BoxConstraints(), ), onChanged: (val) { if (val != null) { print("Selected ClaimsType : ${val['claim_type']}"); print("Id: ${val['id']}"); selectedClaimsType = val['id']; // controllers['agentId']?.text = val['agent_code']; // agentId = agent['id']; } }, ), ), ], ); } Widget buildUploadDocumentClaims(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Upload Document", style: _textStyle), SizedBox(height: 10), ThemedUploadField( backgroundColor: Color(0xFFEDF6F5), hintText: selectedClaimFileNames ?? "Upload Document", txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, onFileSelected: (fileName, file) { print("Picked file: $fileName (${file.size} bytes)"); setState(() { docUploadedFile = file; }); }, ), // Column( // mainAxisAlignment: MainAxisAlignment.end, // crossAxisAlignment: CrossAxisAlignment.end, // children: [ // ThemedUploadField( // hintText: selectedClaimFileNames ?? "Upload Document", // txtwidth: MediaQuery.of(context).size.width * 0.26, // onFileSelected: (fileName, file) { // print("Picked file: $fileName (${file.size} bytes)"); // setState(() { // docUploadedFile = file; // }); // }, // ), // // const SizedBox(height: 5), // // // // if (docUploadedFile != null || passportFileUrlFromApi != null) // // Container( // // // color: Colors.white, // // child: Row( // // children: [ // // GestureDetector( // // onTap: () => apiService.downloadFile( // // apiUrl: // // 'agent/downloadAgentCertificateFile?agent_id=$selectedClaimId', // // apiId: selectedClaimId, // // localFile: docUploadedFile, // // fileName: selectedClaimFileNames, // // ), // // child: Container( // // padding: const EdgeInsets.all(5), // // decoration: BoxDecoration( // // borderRadius: BorderRadius.circular(5), // // color: Color(0xFF425B5B), // // // color: Colors.green.shade300, // // ), // // child: Row( // // children: const [ // // Text( // // "Download", // // style: TextStyle( // // fontSize: 12, // // fontWeight: FontWeight.w200, // // color: Colors.white, // // ), // // ), // // SizedBox(width: 5), // // Icon(Icons.download, size: 13, color: Colors.white), // // ], // // ), // // ), // // ), // // ], // // ), // // ), // ], // ), ], ); } //------------------------- Endrosment Part ----------------------------------- Widget buildEndrosType(context) { Map? selectedEndorsementd = filteredEndrosmentData .firstWhere( (item) => item['id'].toString() == selectedEndorsement, orElse: () => {}, ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Endorsement Type *", style: _textStyle), SizedBox(height: 10), Container( color: Colors.white, width: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, // height: 40, child: DropdownSearch>( key: dropDownKeyEndorsement, selectedItem: selectedEndorsementd.isNotEmpty ? selectedEndorsementd : null, items: (filter, infiniteScrollProps) { return filteredEndrosmentData; }, itemAsString: (val) => val['endorsement_type'].toString(), // what to show compareFn: (item, selectedItem) => item['id'] == selectedItem['id'], // ✅ compare by id validator: (val) { if (val == null) { return "Required"; // ✅ error message } return null; }, decoratorProps: DropDownDecoratorProps( decoration: AppInputDecorations.dropdownDecoration( label: "Select Endorsement Type", ).copyWith( filled: true, fillColor: Color( 0xFFEDF6F5,), // 👈 makes the dropdown input white isDense: true, // 👈 Makes the field compact contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 0), // 👈 Vertical 0 helps center the text ), ), popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: BoxConstraints(maxHeight: 250), menuProps: MenuProps( backgroundColor: Colors.white, // 👈 sets dropdown background to white ), showSearchBox: true, searchFieldProps: TextFieldProps( decoration: InputDecoration( filled: true, fillColor: Colors.white, hintText: "Search Endorsement Type...", enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.white, ), // 👈 Normal border ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.white, width: 1.5, ), // 👈 Focused border ), ), ), // constraints: BoxConstraints(), ), onChanged: (val) { if (val != null) { print("Selected Endorsement : ${val['endorsement_type']}"); print("Id: ${val['id']}"); selectedEndorsement = val['id']; // controllers['agentId']?.text = val['agent_code']; // agentId = agent['id']; } }, ), ), ], ); } Widget buildEndrosPolicyFrom(context) { // Find the selected item from the static list based on your variable Map selectedEndorsementPF = policyFromOptions.firstWhere( (item) => item['policy_from'].toString() == selectedEndrosementPolicyFrom, orElse: () => {}, ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Policy From *", style: _textStyle), const SizedBox(height: 10), Container( color: Colors.white, width: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, child: DropdownSearch>( // Use a unique key for this dropdown key: const ValueKey("policyFromDropdown"), selectedItem: selectedEndorsementPF.isNotEmpty ? selectedEndorsementPF : null, items: (filter, infiniteScrollProps) { return policyFromOptions; // Use the static list }, itemAsString: (val) => val['policy_from'].toString(), compareFn: (item, selectedItem) => item['policy_from'] == selectedItem['policy_from'], // --- REQUIRED VALIDATOR --- validator: (val) { if (val == null || val.isEmpty) { return "Required"; } return null; }, decoratorProps: DropDownDecoratorProps( decoration: AppInputDecorations.dropdownDecoration( label: "Select Policy From", ).copyWith( filled: true, fillColor: const Color(0xFFEDF6F5), // 👈 makes the dropdown input white isDense: true, // 👈 Makes the field compact contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 0), // 👈 Vertical 0 helps center the text ), ), popupProps: PopupProps.menu( fit: FlexFit.loose, constraints: const BoxConstraints(maxHeight: 150), // Shorter for 2 items menuProps: const MenuProps( backgroundColor: Colors.white, ), showSearchBox: true, // Disabled search as there are only 2 items searchFieldProps: TextFieldProps( decoration: InputDecoration( filled: true, fillColor: Colors.white, hintText: "Search Policy From...", enabledBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.white, ), // 👈 Normal border ), focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: Colors.white, width: 1.5, ), // 👈 Focused border ), ), ), ), onChanged: (val) { if (val != null) { setState(() { print("Selected Policy From: ${val['policy_from']}"); selectedEndrosementPolicyFrom = val['policy_from']; }); } }, ), ), ], ); } Widget buildEndrosRemarks(context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Remarks *", style: _textStyle), SizedBox(height: 10), ThemedFormField( backgroundColor: Color(0xFFEDF6F5), controller: controllers['remarks']!, validator: (value) => Validators.requiredField(value, "remarks"), txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, // keyboardType: TextInputType.multiline, // txtheight: 45, ), ], ); } Widget buildUploadDocumentEndorsment(context) { return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text("Upload Document", style: _textStyle), SizedBox(width: 10), ThemedUploadField( backgroundColor: Color(0xFFEDF6F5), hintText: selectedFileNames ?? "Upload Document", txtwidth: ResponsiveLayout.isMobile(context) ? null : MediaQuery.of(context).size.width * 0.26, onFileSelected: (fileName, file) { print("Picked file: $fileName (${file.size} bytes)"); setState(() { docUploadedEndorsFile = file; }); }, ), // Column( // mainAxisAlignment: MainAxisAlignment.end, // crossAxisAlignment: CrossAxisAlignment.end, // children: [ // ThemedUploadField( // hintText: selectedFileNames ?? "Upload Document", // txtwidth: MediaQuery.of(context).size.width * 0.26, // onFileSelected: (fileName, file) { // print("Picked file: $fileName (${file.size} bytes)"); // setState(() { // docUploadedEndorsFile = file; // }); // }, // ), // // const SizedBox(height: 5), // // // // if (docUploadedEndorsFile != null || passportEndorsFileUrlFromApi != null) // // Container( // // // color: Colors.white, // // child: Row( // // children: [ // // // // GestureDetector( // // onTap: () => apiService.downloadFile( // // apiUrl: // // 'agent/downloadAgentCertificateFile?agent_id=$selectedEndorsId', // // apiId: selectedEndorsId, // // localFile: docUploadedEndorsFile, // // fileName: selectedFileNames, // // ), // // child: Container( // // padding: const EdgeInsets.all(5), // // decoration: BoxDecoration( // // borderRadius: BorderRadius.circular(5), // // color: Color(0xFF425B5B), // // // color: Colors.green.shade300, // // ), // // child: Row( // // children: const [ // // Text( // // "Download", // // style: TextStyle( // // fontSize: 12, // // fontWeight: FontWeight.w200, // // color: Colors.white, // // ), // // ), // // SizedBox(width: 5), // // Icon(Icons.download, size: 13, color: Colors.white), // // ], // // ), // // ), // // ), // // ], // // ), // // ), // ], // ), ], ); } // ------------------- STyle --------------------------------- static final TextStyle _textStyle = TextStyle( fontSize: 12, fontWeight: FontWeight.w600, ); static final TextStyle _textStyle1 = TextStyle( fontSize: 9, fontWeight: FontWeight.w400, ); }