trip highlighter

This commit is contained in:
venbaittech 2025-06-19 17:19:54 +05:30
parent 691ba181c6
commit 7f897a7296
6 changed files with 1134 additions and 823 deletions

View File

@ -415,8 +415,9 @@ class _LoginWidgetState extends State<LoginWidget> {
// fit: BoxFit.contain,
// ),
Container(
width: 200, // Optional: control size
height: 100,
// color: Colors.yellow,
width: 120, // Optional: control size
height: 200,
),
Expanded(
child: Container(

View File

@ -988,6 +988,19 @@ class FlightScreenState extends State<FlightScreen> {
child: SizedBox(
height: 40,
width: double.infinity,
child: Focus(
focusNode: focusNodes["_tripType1FocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["_tripType1Focused"] = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
focusNodes["_tripType1FocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
items:
purposeList
@ -1012,7 +1025,8 @@ class FlightScreenState extends State<FlightScreen> {
textControllers["_time${i}Controller"]?.clear();
// Also clear the actual selected data (not just the text in controllers)
selectedFrom[i] = null; // Assuming null means no selection
selectedFrom[i] =
null; // Assuming null means no selection
selectedTo[i] = null;
}
});
@ -1048,6 +1062,8 @@ class FlightScreenState extends State<FlightScreen> {
),
),
),
),
),
// DropdownButtonFormField<String>(
// isExpanded: true,
@ -1419,9 +1435,24 @@ class FlightScreenState extends State<FlightScreen> {
child:
isCountryLoading
? Center(child: CircularProgressIndicator())
: DropdownSearch<String>(
: Focus(
focusNode: focusNodes["_from${index}FocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["_from${index}Focused"] = hasFocus;
});
},
child: GestureDetector(
//
onTap: () {
// Request focus when user taps
focusNodes["_from${index}FocusNode"]
?.requestFocus();
},
child: DropdownSearch<String>(
enabled:
!(selectedTripType == "Roundtrip" && index == 2),
!(selectedTripType == "Roundtrip" &&
index == 2),
selectedItem:
selectedFrom[index] != null
? countryMap[selectedFrom[index]]
@ -1429,7 +1460,8 @@ class FlightScreenState extends State<FlightScreen> {
popupProps: PopupProps.menu(
fit: FlexFit.loose,
constraints: BoxConstraints(maxHeight: 220),
showSearchBox: true, // Enables search functionality
showSearchBox:
true, // Enables search functionality
searchFieldProps: TextFieldProps(
decoration: InputDecoration(
hintText: "Search...",
@ -1440,11 +1472,14 @@ class FlightScreenState extends State<FlightScreen> {
),
style: TextStyle(fontSize: 12),
),
menuProps: MenuProps(backgroundColor: Colors.white),
menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder: (context, item, isSelected) {
final parts = item.split('\n');
final cityAndCode = parts[0];
final airport = parts.length > 1 ? parts[1] : '';
final airport =
parts.length > 1 ? parts[1] : '';
// Extract city and code from "City (CODE)"
final cityMatch = RegExp(
@ -1459,7 +1494,8 @@ class FlightScreenState extends State<FlightScreen> {
vertical: 6.0,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment:
@ -1513,7 +1549,9 @@ class FlightScreenState extends State<FlightScreen> {
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(horizontal: 1),
contentPadding: EdgeInsets.symmetric(
horizontal: 1,
),
),
),
dropdownBuilder: (context, selectedItem) {
@ -1571,6 +1609,8 @@ class FlightScreenState extends State<FlightScreen> {
},
),
),
),
),
// child: SizedBox(
// height: 40,
@ -1941,6 +1981,7 @@ class FlightScreenState extends State<FlightScreen> {
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_date${index}Focused"] ?? false,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.11 : null,
child: SizedBox(
@ -1994,7 +2035,7 @@ class FlightScreenState extends State<FlightScreen> {
),
SizedBox(height: 5),
CustomTextFieldItnerarySubWrapper(
isFocused: focusStates["_timeFocused"] ?? false,
isFocused: focusStates["_time${index}Focused"] ?? false,
isDesktop: isDesktop,
width: isDesktop ? MediaQuery.of(context).size.width * 0.1 : null,
child: SizedBox(
@ -2013,6 +2054,7 @@ class FlightScreenState extends State<FlightScreen> {
); // Force rebuild to show the error immediately
});
},
child: AbsorbPointer(
child: TextField(
focusNode: focusNodes["_time${index}FocusNode"],
@ -2034,6 +2076,19 @@ class FlightScreenState extends State<FlightScreen> {
),
),
),
// Focus(
// focusNode: focusNodes["_time${index}FocusNode"],
// onFocusChange: (hasFocus) {
// setState(() {
// focusStates["_time${index}Focused"] = hasFocus;
// });
// },
// child: GestureDetector(
// //
// onTap: () {
// // Request focus when user taps
// focusNodes["_time${index}FocusNode"]?.requestFocus();
// },
),
),
),

View File

@ -34,10 +34,10 @@ class _MailSettingState extends State<MailSetting> {
List<String> dataHeader = [
"host",
"userName",
"password",
"mail_password",
"port",
"senderEmail",
"toEmail"
"toEmail",
];
Map<String, dynamic> getMailData() => mailData;
@ -46,7 +46,7 @@ class _MailSettingState extends State<MailSetting> {
final data = {
"mail_host": controllers["host"]?.text,
"mail_user_name": controllers["userName"]?.text,
"mail_password": controllers["password"]?.text,
"mail_password": controllers["mail_password"]?.text,
"mail_port": controllers["port"]?.text,
"sender_email": controllers["senderEmail"]?.text,
"to_mail": controllers["toEmail"]?.text,
@ -97,7 +97,7 @@ class _MailSettingState extends State<MailSetting> {
widget.initialMailData['sender_email'] ?? '';
controllers["userName"]?.text =
widget.initialMailData['mail_user_name'] ?? '';
controllers["password"]?.text =
controllers["mail_password"]?.text =
widget.initialMailData['mail_password'] ?? '';
controllers["host"]?.text = widget.initialMailData['mail_host'] ?? '';
controllers["port"]?.text =
@ -181,8 +181,9 @@ class _MailSettingState extends State<MailSetting> {
// Email validation
if (data["to_mail"] != null && data["to_mail"].toString().isNotEmpty) {
if (!RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
.hasMatch(data["to_mail"].toString())) {
if (!RegExp(
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
).hasMatch(data["to_mail"].toString())) {
errorMessages["to_mail"] =
"Invalid email format"; // Invalid email format
}
@ -191,8 +192,9 @@ class _MailSettingState extends State<MailSetting> {
// Email validation
if (data["sender_email"] != null &&
data["sender_email"].toString().isNotEmpty) {
if (!RegExp(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
.hasMatch(data["sender_email"].toString())) {
if (!RegExp(
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
).hasMatch(data["sender_email"].toString())) {
errorMessages["sender_email"] =
"Invalid email format"; // Invalid email format
}
@ -228,13 +230,15 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
@ -249,8 +253,8 @@ class _MailSettingState extends State<MailSetting> {
if (value.trim().isEmpty) {
errorMessages["sender_email"] = "Required";
} else if (!RegExp(
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
.hasMatch(value)) {
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
).hasMatch(value)) {
errorMessages["sender_email"] =
"Invalid email format";
}
@ -259,7 +263,9 @@ class _MailSettingState extends State<MailSetting> {
decoration: InputDecoration(
labelText: "sender email",
labelStyle: GoogleFonts.poppins(
fontSize: 12, color: Colors.grey),
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -278,9 +284,7 @@ class _MailSettingState extends State<MailSetting> {
),
],
),
SizedBox(
height: 10,
),
SizedBox(height: 10),
widget.isDesktop
? Row(
@ -292,22 +296,16 @@ class _MailSettingState extends State<MailSetting> {
children: _buildMailFirstRow(widget.isDesktop),
),
SizedBox(
height: 10,
),
SizedBox(height: 10),
widget.isDesktop
? Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: _buildMailSecondRow(widget.isDesktop),
)
: Column(
children: _buildMailSecondRow(widget.isDesktop),
),
: Column(children: _buildMailSecondRow(widget.isDesktop)),
SizedBox(
height: 10,
),
SizedBox(height: 10),
Container(
// color: Color(0xFFF7F7FB),
@ -317,7 +315,9 @@ class _MailSettingState extends State<MailSetting> {
Text(
"Test Mail",
style: GoogleFonts.poppins(
color: Color(0xFF114D8B), fontWeight: FontWeight.w600),
color: Color(0xFF114D8B),
fontWeight: FontWeight.w600,
),
),
],
),
@ -327,7 +327,7 @@ class _MailSettingState extends State<MailSetting> {
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
children: _buildTestMail(widget.isDesktop),
)
),
],
),
),
@ -344,13 +344,15 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
@ -364,8 +366,10 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "user name",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -390,45 +394,54 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
height: 40,
child: TextField(
// focusNode: _destinationFocusNode,
controller: controllers["password"],
controller: controllers["mail_password"],
onChanged: (value) {
_clearError("mail_password");
},
obscureText: _obscurePassword,
// obscureText: _obscurePassword,
autofillHints: const [
// AutofillHints.newPassword,
], // <--- This is key
enableSuggestions: false, // <--- Disable suggestions
autocorrect: false,
style: TextStyle(fontSize: 12),
decoration: InputDecoration(
labelText: "password",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
labelText: "Access Key",
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
suffixIcon: IconButton(
icon: Icon(
_obscurePassword
? Icons.visibility_off
: Icons.visibility,
size: 16,
),
onPressed: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
),
// suffixIcon: IconButton(
// icon: Icon(
// _obscurePassword
// ? Icons.visibility_off
// : Icons.visibility,
// size: 16,
// ),
// onPressed: () {
// setState(() {
// _obscurePassword = !_obscurePassword;
// });
// },
// ),
),
),
),
@ -455,13 +468,15 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
@ -475,8 +490,10 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "host",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -501,13 +518,15 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
@ -521,8 +540,10 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "port",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -552,13 +573,15 @@ class _MailSettingState extends State<MailSetting> {
style: GoogleFonts.poppins(
fontSize: 12,
fontWeight: FontWeight.w600,
color: Color(0xFF575A74)),
color: Color(0xFF575A74),
),
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isDesktop: widget.isDesktop,
width: widget.isDesktop
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.34
: MediaQuery.of(context).size.width * 0.85,
child: SizedBox(
@ -573,16 +596,18 @@ class _MailSettingState extends State<MailSetting> {
if (value.trim().isEmpty) {
errorMessages["to_mail"] = "Required";
} else if (!RegExp(
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")
.hasMatch(value)) {
r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
).hasMatch(value)) {
errorMessages["to_mail"] = "Invalid email format";
}
},
style: GoogleFonts.poppins(fontSize: 12),
decoration: InputDecoration(
labelText: "To mail",
labelStyle:
GoogleFonts.poppins(fontSize: 12, color: Colors.grey),
labelStyle: GoogleFonts.poppins(
fontSize: 12,
color: Colors.grey,
),
floatingLabelBehavior: FloatingLabelBehavior.never,
border: InputBorder.none,
contentPadding: EdgeInsets.symmetric(vertical: 16),
@ -599,17 +624,16 @@ class _MailSettingState extends State<MailSetting> {
],
],
),
SizedBox(
width: 10,
),
SizedBox(width: 10),
Column(
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Color(0xFF114D8B), // Keep original color
foregroundColor: Colors.white, // Keep original color
disabledBackgroundColor:
Color(0xFF114D8B), // Ensure color remains when disabled
disabledBackgroundColor: Color(
0xFF114D8B,
), // Ensure color remains when disabled
disabledForegroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
@ -620,10 +644,8 @@ class _MailSettingState extends State<MailSetting> {
onPressed: () {
handleTestMailSubmit();
},
child: Text(
"Test Email",
style: GoogleFonts.poppins(fontSize: 12),
))
child: Text("Test Email", style: GoogleFonts.poppins(fontSize: 12)),
),
],
),
];

View File

@ -383,11 +383,11 @@ class CreateNewPlansState extends State<CreateNewPlan> {
final TextEditingController _descriptionController = TextEditingController();
final TextEditingController _remarksController = TextEditingController();
final FocusNode _tripTitleFocusNode = FocusNode();
final FocusNode _descriptionFocusNode = FocusNode(); // Declare FocusNode
// final FocusNode _tripTitleFocusNode = FocusNode();
// final FocusNode _descriptionFocusNode = FocusNode(); // Declare FocusNode
bool _isdescriptionFocused = false;
bool _isTripTitleFocused = false;
// bool _isdescriptionFocused = false;
// bool _isTripTitleFocused = false;
late String _selectedOption = "Option 1";
// late String? _selectedIsBillable = "Billable";
@ -450,6 +450,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
List<Map<String, dynamic>> planStatusList = [];
Map<String, FocusNode> focusNodes = {};
Map<String, bool> focusStates = {};
late bool isApproverApproved = false;
late bool isApproverRejected = false;
@ -483,6 +486,16 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// "status_value": statusValue,
};
List<String> dataHeader = [
"trip_planned",
"trip_type",
"cost_center_id",
"is_billable",
"purpose_of_travel",
"description",
"functional_department",
];
void handleItineraryUpdate(String type, List<Map<String, dynamic>> newList) {
setState(() {
switch (type) {
@ -530,34 +543,62 @@ class CreateNewPlansState extends State<CreateNewPlan> {
}
flightTripTypeNotifier = ValueNotifier(null);
for (var field in dataHeader) {
focusNodes["${field}FocusNode"] = FocusNode();
focusStates["${field}Focused"] = false;
}
print("Focus Nodes KeysII: ${focusNodes.keys.toList()}");
print("Focus States KeysII: ${focusStates.keys.toList()}");
for (var key in focusNodes.keys) {
_addFocusListener(focusNodes[key]!, (focus) {
setState(() {
focusStates[key.replaceFirst("FocusNode", "Focused")] = focus;
});
});
}
fetchUserDetails();
fetchPlans();
fetchCostCenter();
fetchCountryList();
_tripTitleFocusNode.addListener(() {
setState(() {
_isTripTitleFocused = _tripTitleFocusNode.hasFocus;
});
});
_descriptionFocusNode.addListener(() {
setState(() {
_isdescriptionFocused = _descriptionFocusNode.hasFocus;
});
});
// _tripTitleFocusNode.addListener(() {
// setState(() {
// _isTripTitleFocused = _tripTitleFocusNode.hasFocus;
// });
// });
//
// _descriptionFocusNode.addListener(() {
// setState(() {
// _isdescriptionFocused = _descriptionFocusNode.hasFocus;
// });
// });
handleUpdateData();
}
@override
void dispose() {
_tripTitleFocusNode.dispose();
_descriptionFocusNode.dispose();
for (var node in focusNodes.values) {
node.dispose();
}
// _tripTitleFocusNode.dispose();
// _descriptionFocusNode.dispose();
super.dispose();
}
void _addFocusListener(FocusNode node, Function(bool) updateState) {
node.addListener(() {
setState(() {
updateState(node.hasFocus);
});
});
}
void handleSelectedUser() {
if (widget.selectedPlanData != null) {
setState(() {
@ -1236,7 +1277,29 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// await postPlanData(planData);
print("Hide loader at: ${DateTime.now()}");
// Close loading dialog (ONLY if still mounted)
if (mounted) Navigator.of(context, rootNavigator: true).pop();
Future.delayed(Duration(seconds: 12), () {
if (mounted) {
Navigator.of(context, rootNavigator: true).pop();
final currentUri =
GoRouterState.of(
context,
).uri.toString(); // safer than `.location`
print("currentUri - $currentUri");
if (currentUri == "/allTrips/trips") {
context.go('/listAllPlan');
} else if (currentUri == "/createPlan") {
context.go('/listPlan');
} else {
widget.isApprover
? context.go('/approvallist')
: context.go('/listPlan');
}
}
});
// if (mounted) Navigator.of(context, rootNavigator: true).pop();
}
});
}
@ -1741,11 +1804,23 @@ class CreateNewPlansState extends State<CreateNewPlan> {
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false,
isFocused: focusStates["trip_typeFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
width: double.infinity,
child: Focus(
focusNode: focusNodes["trip_typeFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["trip_typeFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["trip_typeFocusNode"]?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
popupProps: PopupProps.menu(
showSearchBox: false, // Optionally enable search
@ -1818,7 +1893,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
_selectedTripType =
newItem['dropdown_key'].toString();
// Notify FlightScreen
flightTripTypeNotifier.value = _selectedTripType;
flightTripTypeNotifier.value =
_selectedTripType;
print(
"flightTripTypeNotifier- ${flightTripTypeNotifier.value}",
);
@ -1833,6 +1909,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
),
),
),
),
// isMobile
// ? SingleChildScrollView(
// scrollDirection: Axis.horizontal,
@ -1873,15 +1951,28 @@ class CreateNewPlansState extends State<CreateNewPlan> {
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: focusStates["is_billableFocused"] ?? false,
width:
widget.isDesktop
? MediaQuery.of(context).size.width * 0.2
: null,
isFocused: false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
width: double.infinity,
child: Focus(
focusNode: focusNodes["is_billableFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["is_billableFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["is_billableFocusNode"]?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
popupProps: PopupProps.menu(
showSearchBox: false,
@ -1962,6 +2053,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
),
),
),
),
// Row(
// children: purposeList.map<Widget>((item) {
@ -2132,7 +2225,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
isFocused:
focusStates["cost_center_idFocused"] ??
false, // Dropdown doesn't use focus
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
@ -2145,7 +2240,20 @@ class CreateNewPlansState extends State<CreateNewPlan> {
child: CircularProgressIndicator(),
),
)
: DropdownSearch<String>(
: Focus(
focusNode: focusNodes["cost_center_idFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["cost_center_idFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["cost_center_idFocusNode"]
?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem: costCenterMap[selectedCostCenterId],
enabled: !widget.isViewMode,
popupProps: PopupProps.menu(
@ -2166,7 +2274,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
style: GoogleFonts.poppins(fontSize: 13),
),
menuProps: MenuProps(backgroundColor: Colors.white),
menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
@ -2195,7 +2305,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
if (selectedItem == null ||
selectedItem.isEmpty) {
return Text(
"Select Purpose", // fallback text
style: GoogleFonts.poppins(
@ -2231,6 +2342,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
},
),
),
),
),
// child: SizedBox(
// height: 45, // Set appropriate height
@ -2286,8 +2399,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
width:
isDesktop
? MediaQuery.of(context).size.width * 0.2
: MediaQuery.of(context).size.width * 0.85,
isFocused: false, // Dropdown doesn't use focus
: MediaQuery.of(context).size.width * 0.88,
isFocused: focusStates["purpose_of_travelFocused"] ?? false,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35, // Set appropriate height
@ -2297,12 +2411,26 @@ class CreateNewPlansState extends State<CreateNewPlan> {
? Center(
child: CircularProgressIndicator(),
) // Show loading inside dropdown
: DropdownSearch<Map<String, dynamic>>(
: Focus(
focusNode: focusNodes["purpose_of_travelFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["purpose_of_travelFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["purpose_of_travelFocusNode"]
?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
selectedItem: purposeList.firstWhere(
(item) =>
item['dropdown_key'].toString() ==
selectedPurpose,
orElse: () => <String, dynamic>{}, // Safe fallback
orElse:
() => <String, dynamic>{}, // Safe fallback
),
items: purposeList,
itemAsString:
@ -2325,7 +2453,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
style: GoogleFonts.poppins(fontSize: 13),
),
menuProps: MenuProps(backgroundColor: Colors.white),
menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
@ -2355,13 +2485,17 @@ class CreateNewPlansState extends State<CreateNewPlan> {
: (Map<String, dynamic>? newValue) {
setState(() {
selectedPurpose =
newValue?['dropdown_key'].toString();
newValue?['dropdown_key']
.toString();
});
print("selectedPurpose - $selectedPurpose");
print(
"selectedPurpose - $selectedPurpose",
);
},
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
if (selectedItem == null ||
selectedItem.isEmpty) {
return Text(
"Select Purpose", // fallback text
style: GoogleFonts.poppins(
@ -2389,6 +2523,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
),
),
),
),
if (validationErrors["purpose_of_travel"] != null)
Padding(
padding: EdgeInsets.only(top: 4),
@ -2446,12 +2582,12 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: false, // Dropdown doesn't use focus
isFocused: focusStates["functional_departmentFocused"] ?? false,
isDesktop: widget.isDesktop,
width:
isDesktop
? MediaQuery.of(context).size.width * 0.2
: MediaQuery.of(context).size.width * 0.85,
: MediaQuery.of(context).size.width * 0.88,
// width: MediaQuery.of(context).size.width * 0.2,
child: SizedBox(
height: 35, // Set appropriate height
@ -2460,12 +2596,27 @@ class CreateNewPlansState extends State<CreateNewPlan> {
? Center(
child: CircularProgressIndicator(),
) // Show loading inside dropdown
: DropdownSearch<Map<String, dynamic>>(
: Focus(
focusNode: focusNodes["functional_departmentFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["functional_departmentFocused"] =
hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["functional_departmentFocusNode"]
?.requestFocus();
},
child: DropdownSearch<Map<String, dynamic>>(
selectedItem: funcDeptList.firstWhere(
(item) =>
item['dropdown_key'].toString() ==
selectedFuncDept,
orElse: () => <String, dynamic>{}, // Safe fallback
orElse:
() => <String, dynamic>{}, // Safe fallback
),
items: funcDeptList,
itemAsString:
@ -2491,7 +2642,9 @@ class CreateNewPlansState extends State<CreateNewPlan> {
13, // 👈 Small font size for selected item
),
),
menuProps: MenuProps(backgroundColor: Colors.white),
menuProps: MenuProps(
backgroundColor: Colors.white,
),
itemBuilder:
(context, item, isSelected) => Padding(
padding: const EdgeInsets.symmetric(
@ -2522,13 +2675,17 @@ class CreateNewPlansState extends State<CreateNewPlan> {
: (Map<String, dynamic>? newValue) {
setState(() {
selectedFuncDept =
newValue?['dropdown_key'].toString();
newValue?['dropdown_key']
.toString();
});
print("selectedFuncDept - $selectedFuncDept");
print(
"selectedFuncDept - $selectedFuncDept",
);
},
dropdownBuilder: (context, selectedItem) {
if (selectedItem == null || selectedItem.isEmpty) {
if (selectedItem == null ||
selectedItem.isEmpty) {
return const Text(
"Select Purpose", // fallback text
style: TextStyle(
@ -2556,6 +2713,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
),
),
),
),
if (validationErrors["functional_department"] != null)
Padding(
padding: EdgeInsets.only(top: 4),
@ -2578,12 +2737,25 @@ class CreateNewPlansState extends State<CreateNewPlan> {
return [
CustomTextFieldWrapper(
isFocused: false,
isFocused: focusStates["trip_plannedFocused"] ?? false,
isDesktop: isDesktop,
layoutColor: widget.layoutColor,
child: SizedBox(
height: 35,
width: double.infinity,
child: Focus(
focusNode: focusNodes["trip_plannedFocusNode"],
onFocusChange: (hasFocus) {
setState(() {
focusStates["trip_plannedFocused"] = hasFocus;
});
},
child: GestureDetector(
onTap: () {
// Request focus when user taps
focusNodes["trip_plannedFocusNode"]?.requestFocus();
},
child: DropdownSearch<String>(
selectedItem:
options.firstWhere(
@ -2601,7 +2773,10 @@ class CreateNewPlansState extends State<CreateNewPlan> {
horizontal: 8.0,
vertical: 6.0,
),
child: Text(item, style: GoogleFonts.poppins(fontSize: 13)),
child: Text(
item,
style: GoogleFonts.poppins(fontSize: 13),
),
),
),
dropdownDecoratorProps: DropDownDecoratorProps(
@ -2640,6 +2815,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
),
),
),
),
];
}
@ -2892,12 +3069,13 @@ class CreateNewPlansState extends State<CreateNewPlan> {
),
SizedBox(height: 5),
CustomTextFieldWrapper(
isFocused: _isTripTitleFocused,
isFocused: false,
// isFocused: _isTripTitleFocused,
isDesktop: widget.isDesktop,
child: SizedBox(
height: 35,
child: TextField(
focusNode: _tripTitleFocusNode,
// focusNode: _tripTitleFocusNode,
controller: _tripTitleController,
style: TextStyle(fontSize: 12),
enabled: !widget.isViewMode,
@ -2963,8 +3141,8 @@ class CreateNewPlansState extends State<CreateNewPlan> {
// ),
// ),
CustomTextFieldWrapper(
// isFocused: false,
isFocused: _isdescriptionFocused,
isFocused: focusStates["descriptionFocused"] ?? false,
// isFocused: _isdescriptionFocused,
// width: isDesktop
// ? MediaQuery.of(context).size.width * 0.38
// : MediaQuery.of(context).size.width * 0.85,
@ -2972,7 +3150,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
child: SizedBox(
height: 55,
child: TextField(
focusNode: _descriptionFocusNode,
focusNode: focusNodes["descriptionFocusNode"],
controller: _descriptionController,
maxLines: 2,
keyboardType: TextInputType.multiline,
@ -3059,7 +3237,7 @@ class CreateNewPlansState extends State<CreateNewPlan> {
width: isDesktop ? MediaQuery.of(context).size.width * 0.2 : 180,
height: 35,
child: TextField(
focusNode: _tripTitleFocusNode,
// focusNode: _tripTitleFocusNode,
controller: _tripTitleController,
// style: TextStyle(fontSize: 12),
style: GoogleFonts.poppins(

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:frontend/utils/colorOpcity.dart';
import '../utils/auth_utils.dart';
class CustomTextFieldWrapper extends StatefulWidget {
final Widget child;
final bool isFocused;
@ -30,6 +32,28 @@ class CustomTextFieldWrapper extends StatefulWidget {
}
class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
Color? layoutColor;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
Color getColorWithOpacity(Color color, double opacity) {
final int alpha = (opacity * 255).round().clamp(0, 255);
return Color.fromARGB(
@ -43,22 +67,23 @@ class _CustomTextFieldWrapperState extends State<CustomTextFieldWrapper> {
@override
Widget build(BuildContext context) {
return Container(
width: widget.width ?? // Use custom width if provided, else default
width:
widget.width ?? // Use custom width if provided, else default
(widget.isDesktop
? MediaQuery.of(context).size.width * 0.3
: MediaQuery.of(context).size.width * 0.85),
: MediaQuery.of(context).size.width * 0.88),
padding: widget.padding,
decoration: BoxDecoration(
color: widget.isFocused
? (widget.layoutColor ?? widget.color)
: widget.color,
// color: widget.isFocused
// ? (widget.layoutColor ?? widget.color)
// : widget.color,
color: Colors.white,
borderRadius: widget.borderRadius ?? BorderRadius.circular(8),
border: Border.all(
color: widget.isFocused
? (widget.layoutColor ?? Colors.blueAccent)
: Color(0xFFF5F5F5),
width: widget.isFocused ? 1.5 : 1.5,
color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6),
// color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
width: widget.isFocused ? 1.0 : 0.5,
),
// boxShadow: widget.isFocused
// ? [

View File

@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import '../utils/auth_utils.dart';
class CustomTextFieldItnerarySubWrapper extends StatefulWidget {
final Widget child;
final bool isFocused;
@ -21,33 +23,61 @@ class CustomTextFieldItnerarySubWrapper extends StatefulWidget {
});
@override
_CustomTextFieldItnerarySubWrapperState createState() => _CustomTextFieldItnerarySubWrapperState();
_CustomTextFieldItnerarySubWrapperState createState() =>
_CustomTextFieldItnerarySubWrapperState();
}
class _CustomTextFieldItnerarySubWrapperState extends State<CustomTextFieldItnerarySubWrapper> {
class _CustomTextFieldItnerarySubWrapperState
extends State<CustomTextFieldItnerarySubWrapper> {
Color? layoutColor;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
loadInitialData();
});
}
void loadInitialData() async {
String? layoutString = await getLayoutColor();
setState(() {
layoutColor =
layoutString != null
? Color(int.parse(layoutString))
: Colors.redAccent;
});
}
@override
Widget build(BuildContext context) {
return Container(
width: widget.width ?? // Use custom width if provided, else default
width:
widget.width ?? // Use custom width if provided, else default
(widget.isDesktop
? MediaQuery.of(context).size.width * 0.15
: MediaQuery.of(context).size.width * 0.8),
padding: widget.padding,
decoration: BoxDecoration(
color: widget.color,
borderRadius: BorderRadius.circular(10),
color: Colors.white,
border: Border.all(
color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
width: widget.isFocused ? 2.0 : 0.5,
color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6),
// color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6),
width: widget.isFocused ? 1.0 : 0.5,
),
boxShadow: widget.isFocused
boxShadow:
widget.isFocused
? [
BoxShadow(
color: Color.fromRGBO(120, 180, 252, 0.3),
color: Colors.white,
// color: Color.fromRGBO(120, 180, 252, 0.3),
blurRadius: 10,
spreadRadius: 2,
offset: Offset(0, 4),
),
]
: [],