From 7f897a7296e2f918d5b036ba508d9bf0fda28424 Mon Sep 17 00:00:00 2001 From: venbaittech Date: Thu, 19 Jun 2025 17:19:54 +0530 Subject: [PATCH] trip highlighter --- .../authentication/login/login_widget.dart | 5 +- lib/Screens/itnerary/flights.dart | 435 ++++--- lib/Screens/organization/mailSettings.dart | 260 ++-- lib/Screens/plans/create_plans.dart | 1148 ++++++++++------- lib/widgets/custom_text_field.dart | 43 +- lib/widgets/custom_text_itnerary_sub.dart | 66 +- 6 files changed, 1134 insertions(+), 823 deletions(-) diff --git a/lib/Screens/authentication/login/login_widget.dart b/lib/Screens/authentication/login/login_widget.dart index e67651c..783cbc8 100644 --- a/lib/Screens/authentication/login/login_widget.dart +++ b/lib/Screens/authentication/login/login_widget.dart @@ -415,8 +415,9 @@ class _LoginWidgetState extends State { // fit: BoxFit.contain, // ), Container( - width: 200, // Optional: control size - height: 100, + // color: Colors.yellow, + width: 120, // Optional: control size + height: 200, ), Expanded( child: Container( diff --git a/lib/Screens/itnerary/flights.dart b/lib/Screens/itnerary/flights.dart index 18212dc..12ec138 100644 --- a/lib/Screens/itnerary/flights.dart +++ b/lib/Screens/itnerary/flights.dart @@ -988,64 +988,80 @@ class FlightScreenState extends State { child: SizedBox( height: 40, width: double.infinity, - child: DropdownSearch( - items: - purposeList - .map((item) => item['dropdown_value'] as String) - .toList(), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 1), - ), - ), - onChanged: (newValue) { + child: Focus( + focusNode: focusNodes["_tripType1FocusNode"], + onFocusChange: (hasFocus) { setState(() { - selectedTripType = newValue; - if (selectedTripType != "Multitrip") { - multiTripRowCount = 1; - } - errorMessages.clear(); - - for (int i = 1; i <= rowCount; i++) { - textControllers["_date${i}Controller"]?.clear(); - 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 - selectedTo[i] = null; - } + focusStates["_tripType1Focused"] = hasFocus; }); - print( - "Updating form data: Flight -> trip_type -> $selectedTripType", - ); - _initializeFields(); }, - selectedItem: selectedTripType, - dropdownBuilder: - (context, selectedItem) => Align( - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select", - style: TextStyle(fontSize: 12), + child: GestureDetector( + // + onTap: () { + // Request focus when user taps + focusNodes["_tripType1FocusNode"]?.requestFocus(); + }, + child: DropdownSearch( + items: + purposeList + .map((item) => item['dropdown_value'] as String) + .toList(), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 1), ), ), - popupProps: PopupProps.menu( - constraints: BoxConstraints(maxHeight: 100), - menuProps: MenuProps(backgroundColor: Colors.white), - itemBuilder: - (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, + onChanged: (newValue) { + setState(() { + selectedTripType = newValue; + if (selectedTripType != "Multitrip") { + multiTripRowCount = 1; + } + errorMessages.clear(); + + for (int i = 1; i <= rowCount; i++) { + textControllers["_date${i}Controller"]?.clear(); + 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 + selectedTo[i] = null; + } + }); + print( + "Updating form data: Flight -> trip_type -> $selectedTripType", + ); + _initializeFields(); + }, + selectedItem: selectedTripType, + dropdownBuilder: + (context, selectedItem) => Align( + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select", + style: TextStyle(fontSize: 12), + ), ), - child: Text( - item, - style: TextStyle( - fontSize: 13, - ), // Custom text size for dropdown items - ), - ), + popupProps: PopupProps.menu( + constraints: BoxConstraints(maxHeight: 100), + menuProps: MenuProps(backgroundColor: Colors.white), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: TextStyle( + fontSize: 13, + ), // Custom text size for dropdown items + ), + ), + ), + ), ), ), @@ -1419,156 +1435,180 @@ class FlightScreenState extends State { child: isCountryLoading ? Center(child: CircularProgressIndicator()) - : DropdownSearch( - enabled: - !(selectedTripType == "Roundtrip" && index == 2), - selectedItem: - selectedFrom[index] != null - ? countryMap[selectedFrom[index]] - : null, - popupProps: PopupProps.menu( - fit: FlexFit.loose, - constraints: BoxConstraints(maxHeight: 220), - showSearchBox: true, // Enables search functionality - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search...", - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - vertical: 1, + : 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( + enabled: + !(selectedTripType == "Roundtrip" && + index == 2), + selectedItem: + selectedFrom[index] != null + ? countryMap[selectedFrom[index]] + : null, + popupProps: PopupProps.menu( + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 220), + showSearchBox: + true, // Enables search functionality + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search...", + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 1, + ), + ), + style: TextStyle(fontSize: 12), ), - ), - style: TextStyle(fontSize: 12), - ), - 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] : ''; - - // Extract city and code from "City (CODE)" - final cityMatch = RegExp( - r'^(.*)\s+\(([^)]+)\)$', - ).firstMatch(cityAndCode); - final city = cityMatch?.group(1) ?? ''; - final code = cityMatch?.group(2) ?? ''; - - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, + menuProps: MenuProps( + backgroundColor: Colors.white, ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, + itemBuilder: (context, item, isSelected) { + final parts = item.split('\n'); + final cityAndCode = parts[0]; + final airport = + parts.length > 1 ? parts[1] : ''; + + // Extract city and code from "City (CODE)" + final cityMatch = RegExp( + r'^(.*)\s+\(([^)]+)\)$', + ).firstMatch(cityAndCode); + final city = cityMatch?.group(1) ?? ''; + final code = cityMatch?.group(2) ?? ''; + + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, children: [ - Text( - city, - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - ), + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + city, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + Text( + code, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + ), + ), + ], ), + const SizedBox(height: 1), Text( - code, + airport, style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, + fontSize: 11, + color: Colors.grey, + overflow: TextOverflow.ellipsis, ), ), ], ), - const SizedBox(height: 1), - Text( - airport, - style: const TextStyle( - fontSize: 11, - color: Colors.grey, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ); - }, + ); + }, - // itemBuilder: - // (context, item, isSelected) => Padding( - // padding: const EdgeInsets.symmetric( - // horizontal: 8.0, - // vertical: 6.0, - // ), - // child: Text( - // item, - // style: TextStyle( - // fontSize: 13, - // ), // 👈 Set your desired text size here - // ), - // ), - ), - items: countryMap.values.toList(), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 1), + // itemBuilder: + // (context, item, isSelected) => Padding( + // padding: const EdgeInsets.symmetric( + // horizontal: 8.0, + // vertical: 6.0, + // ), + // child: Text( + // item, + // style: TextStyle( + // fontSize: 13, + // ), // 👈 Set your desired text size here + // ), + // ), + ), + items: countryMap.values.toList(), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 1, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null) { + return Align( + alignment: Alignment.centerLeft, + child: const Text( + "Select", + style: TextStyle(fontSize: 12), + ), + ); + } + + final parts = selectedItem.split('\n'); + final cityAndCode = parts[0]; + final airport = parts.length > 1 ? parts[1] : ''; + + return Align( + alignment: Alignment.centerLeft, + child: Text( + cityAndCode ?? "Select", + style: const TextStyle(fontSize: 12), + ), + ); + }, + + // dropdownBuilder: + // (context, selectedItem) => Align( + // alignment: Alignment.centerLeft, + // child: Text( + // selectedItem ?? "Select", + // style: TextStyle(fontSize: 12), + // ), + // ), + onChanged: (String? newValue) { + setState(() { + errorMessages.remove("from_place_$index"); + // selectedFrom[index] = countryMap.entries + // .firstWhere((entry) => entry.value == newValue) + // .key; + + selectedFrom[index] = + countryMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; + + if (selectedTripType == "Roundtrip") { + print('rounfTo${selectedFrom[index]}'); + // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; + selectedTo[2] = selectedFrom[index]!; + } + }); + }, ), ), - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null) { - return Align( - alignment: Alignment.centerLeft, - child: const Text( - "Select", - style: TextStyle(fontSize: 12), - ), - ); - } - - final parts = selectedItem.split('\n'); - final cityAndCode = parts[0]; - final airport = parts.length > 1 ? parts[1] : ''; - - return Align( - alignment: Alignment.centerLeft, - child: Text( - cityAndCode ?? "Select", - style: const TextStyle(fontSize: 12), - ), - ); - }, - - // dropdownBuilder: - // (context, selectedItem) => Align( - // alignment: Alignment.centerLeft, - // child: Text( - // selectedItem ?? "Select", - // style: TextStyle(fontSize: 12), - // ), - // ), - onChanged: (String? newValue) { - setState(() { - errorMessages.remove("from_place_$index"); - // selectedFrom[index] = countryMap.entries - // .firstWhere((entry) => entry.value == newValue) - // .key; - - selectedFrom[index] = - countryMap.entries - .firstWhere( - (entry) => entry.value == newValue, - ) - .key; - - if (selectedTripType == "Roundtrip") { - print('rounfTo${selectedFrom[index]}'); - // textControllers["_to${index+1}Controller"]?.text = selectedFrom[index]!; - selectedTo[2] = selectedFrom[index]!; - } - }); - }, ), ), @@ -1941,6 +1981,7 @@ class FlightScreenState extends State { 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 { ), 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 { ); // ✅ Force rebuild to show the error immediately }); }, + child: AbsorbPointer( child: TextField( focusNode: focusNodes["_time${index}FocusNode"], @@ -2034,6 +2076,19 @@ class FlightScreenState extends State { ), ), ), + // 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(); + // }, ), ), ), diff --git a/lib/Screens/organization/mailSettings.dart b/lib/Screens/organization/mailSettings.dart index f8af175..b75af06 100644 --- a/lib/Screens/organization/mailSettings.dart +++ b/lib/Screens/organization/mailSettings.dart @@ -34,10 +34,10 @@ class _MailSettingState extends State { List dataHeader = [ "host", "userName", - "password", + "mail_password", "port", "senderEmail", - "toEmail" + "toEmail", ]; Map getMailData() => mailData; @@ -46,7 +46,7 @@ class _MailSettingState extends State { 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 { 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 { // 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 { // 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 } @@ -226,17 +228,19 @@ class _MailSettingState extends State { Text( "Sender Email", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: false, isDesktop: widget.isDesktop, - width: widget.isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.85, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( @@ -249,8 +253,8 @@ class _MailSettingState extends State { 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 { 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,36 +284,28 @@ class _MailSettingState extends State { ), ], ), - SizedBox( - height: 10, - ), + SizedBox(height: 10), widget.isDesktop ? Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: _buildMailFirstRow(widget.isDesktop), - ) + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: _buildMailFirstRow(widget.isDesktop), + ) : Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: _buildMailFirstRow(widget.isDesktop), - ), + crossAxisAlignment: CrossAxisAlignment.center, + 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), - ), + mainAxisAlignment: MainAxisAlignment.spaceBetween, + 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 { 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 { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.end, children: _buildTestMail(widget.isDesktop), - ) + ), ], ), ), @@ -342,17 +342,19 @@ class _MailSettingState extends State { Text( "User Name", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: false, isDesktop: widget.isDesktop, - width: widget.isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.85, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( @@ -364,8 +366,10 @@ class _MailSettingState extends State { 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), @@ -388,47 +392,56 @@ class _MailSettingState extends State { Text( "Password", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: false, isDesktop: widget.isDesktop, - width: widget.isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.85, + 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; + // }); + // }, + // ), ), ), ), @@ -453,17 +466,19 @@ class _MailSettingState extends State { Text( "Host", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: false, isDesktop: widget.isDesktop, - width: widget.isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.85, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( @@ -475,8 +490,10 @@ class _MailSettingState extends State { 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), @@ -499,17 +516,19 @@ class _MailSettingState extends State { Text( "Port", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: false, isDesktop: widget.isDesktop, - width: widget.isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.85, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( @@ -521,8 +540,10 @@ class _MailSettingState extends State { 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), @@ -550,17 +571,19 @@ class _MailSettingState extends State { Text( "Enter Your Mail Id", style: GoogleFonts.poppins( - fontSize: 12, - fontWeight: FontWeight.w600, - color: Color(0xFF575A74)), + fontSize: 12, + fontWeight: FontWeight.w600, + color: Color(0xFF575A74), + ), ), SizedBox(height: 5), CustomTextFieldWrapper( isFocused: false, isDesktop: widget.isDesktop, - width: widget.isDesktop - ? MediaQuery.of(context).size.width * 0.34 - : MediaQuery.of(context).size.width * 0.85, + width: + widget.isDesktop + ? MediaQuery.of(context).size.width * 0.34 + : MediaQuery.of(context).size.width * 0.85, child: SizedBox( height: 40, child: TextField( @@ -573,16 +596,18 @@ class _MailSettingState extends State { 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,31 +624,28 @@ class _MailSettingState extends State { ], ], ), - 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 - disabledForegroundColor: Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - side: BorderSide(color: Color(0xFF114D8B), width: 2), - ), - padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12), + style: ElevatedButton.styleFrom( + backgroundColor: Color(0xFF114D8B), // Keep original color + foregroundColor: Colors.white, // Keep original color + disabledBackgroundColor: Color( + 0xFF114D8B, + ), // Ensure color remains when disabled + disabledForegroundColor: Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + side: BorderSide(color: Color(0xFF114D8B), width: 2), ), - onPressed: () { - handleTestMailSubmit(); - }, - child: Text( - "Test Email", - style: GoogleFonts.poppins(fontSize: 12), - )) + padding: EdgeInsets.symmetric(horizontal: 18, vertical: 12), + ), + onPressed: () { + handleTestMailSubmit(); + }, + child: Text("Test Email", style: GoogleFonts.poppins(fontSize: 12)), + ), ], ), ]; diff --git a/lib/Screens/plans/create_plans.dart b/lib/Screens/plans/create_plans.dart index ea3b6ca..5e23763 100644 --- a/lib/Screens/plans/create_plans.dart +++ b/lib/Screens/plans/create_plans.dart @@ -383,11 +383,11 @@ class CreateNewPlansState extends State { 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 { List> planStatusList = []; + Map focusNodes = {}; + Map focusStates = {}; + late bool isApproverApproved = false; late bool isApproverRejected = false; @@ -483,6 +486,16 @@ class CreateNewPlansState extends State { // "status_value": statusValue, }; + List dataHeader = [ + "trip_planned", + "trip_type", + "cost_center_id", + "is_billable", + "purpose_of_travel", + "description", + "functional_department", + ]; + void handleItineraryUpdate(String type, List> newList) { setState(() { switch (type) { @@ -530,34 +543,62 @@ class CreateNewPlansState extends State { } 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 { // 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,95 +1804,110 @@ class CreateNewPlansState extends State { SizedBox(height: 5), CustomTextFieldWrapper( - isFocused: false, + isFocused: focusStates["trip_typeFocused"] ?? false, isDesktop: widget.isDesktop, child: SizedBox( height: 35, width: double.infinity, - child: DropdownSearch>( - popupProps: PopupProps.menu( - showSearchBox: false, // Optionally enable search - fit: FlexFit.loose, - menuProps: const MenuProps(backgroundColor: Colors.white), - itemBuilder: (context, item, isSelected) { - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 8, - ), - child: Text( - item['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: 12, // 👈 Smaller font size - color: Colors.black, + 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>( + popupProps: PopupProps.menu( + showSearchBox: false, // Optionally enable search + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: (context, item, isSelected) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + child: Text( + item['dropdown_value'] ?? '', + 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, ), ), - ); - }, - ), - 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 Trip Type", + style: GoogleFonts.poppins( + color: Colors.grey, + fontSize: 13, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + selectedItem: tripTypeList.firstWhere( + (item) => item['dropdown_key'] == _selectedTripType, + orElse: () => {}, + ), + itemAsString: (item) => item['dropdown_value'] ?? '', + items: getFilteredTripTypes(), + + // items: + // [ + // {"dropdown_key": "1", "dropdown_value": "Domestic"}, + // {"dropdown_key": "2", "dropdown_value": "International"}, + // ].map>((item) { + // return Map.from(item); + // }).toList(), + onChanged: + widget.isViewMode + ? null + : (Map? newItem) { + if (newItem != null) { + setState(() { + _selectedTripType = + newItem['dropdown_key'].toString(); + // Notify FlightScreen + flightTripTypeNotifier.value = + _selectedTripType; + print( + "flightTripTypeNotifier- ${flightTripTypeNotifier.value}", + ); + fetchTrainFlightClass( + int.parse(_selectedTripType!), + ); + dynamicItineraryKey.currentState + ?.updateSelectedServices(); + }); + } + }, ), ), - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null || selectedItem.isEmpty) { - return Text( - "Select Trip Type", - style: GoogleFonts.poppins( - color: Colors.grey, - fontSize: 13, - ), - ); - } - return Text( - selectedItem['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black, - ), - ); - }, - selectedItem: tripTypeList.firstWhere( - (item) => item['dropdown_key'] == _selectedTripType, - orElse: () => {}, - ), - itemAsString: (item) => item['dropdown_value'] ?? '', - items: getFilteredTripTypes(), - - // items: - // [ - // {"dropdown_key": "1", "dropdown_value": "Domestic"}, - // {"dropdown_key": "2", "dropdown_value": "International"}, - // ].map>((item) { - // return Map.from(item); - // }).toList(), - onChanged: - widget.isViewMode - ? null - : (Map? newItem) { - if (newItem != null) { - setState(() { - _selectedTripType = - newItem['dropdown_key'].toString(); - // Notify FlightScreen - flightTripTypeNotifier.value = _selectedTripType; - print( - "flightTripTypeNotifier- ${flightTripTypeNotifier.value}", - ); - fetchTrainFlightClass( - int.parse(_selectedTripType!), - ); - dynamicItineraryKey.currentState - ?.updateSelectedServices(); - }); - } - }, ), ), ), @@ -1873,92 +1951,107 @@ class CreateNewPlansState extends State { 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: DropdownSearch>( - popupProps: PopupProps.menu( - showSearchBox: false, - fit: FlexFit.loose, - menuProps: const MenuProps(backgroundColor: Colors.white), - itemBuilder: (context, item, isSelected) { - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 8, + child: 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>( + popupProps: PopupProps.menu( + showSearchBox: false, + fit: FlexFit.loose, + menuProps: const MenuProps(backgroundColor: Colors.white), + itemBuilder: (context, item, isSelected) { + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 8, + ), + child: Text( + item['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ), + ); + }, + ), + dropdownDecoratorProps: const DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), ), - child: Text( - item['dropdown_value'] ?? '', + ), + selectedItem: purposeList + .cast>() + .firstWhere( + (item) => item['dropdown_key'] == _selectedIsBillable, + orElse: () => {}, + ), + dropdownButtonProps: const DropdownButtonProps( + icon: Icon(Icons.arrow_drop_down), + ), + // itemAsString: (item) => item['dropdown_value'] ?? '', + // items: purposeList.cast>(), + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || selectedItem.isEmpty) { + return Text( + "Select Billable", // fallback text + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.grey, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', style: GoogleFonts.poppins( fontSize: 12, color: Colors.black, ), - ), - ); - }, - ), - dropdownDecoratorProps: const DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), + ); + }, + + items: + purposeList.map>((item) { + return Map.from( + item, + ); // Ensuring each item is properly cast + }).toList(), + onChanged: + widget.isViewMode + ? null + : (Map? newItem) { + if (newItem != null) { + setState(() { + _selectedIsBillable = + newItem['dropdown_key'].toString(); + }); + } + }, ), ), - selectedItem: purposeList - .cast>() - .firstWhere( - (item) => item['dropdown_key'] == _selectedIsBillable, - orElse: () => {}, - ), - dropdownButtonProps: const DropdownButtonProps( - icon: Icon(Icons.arrow_drop_down), - ), - // itemAsString: (item) => item['dropdown_value'] ?? '', - // items: purposeList.cast>(), - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null || selectedItem.isEmpty) { - return Text( - "Select Billable", // fallback text - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.grey, - ), - ); - } - return Text( - selectedItem['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black, - ), - ); - }, - - items: - purposeList.map>((item) { - return Map.from( - item, - ); // Ensuring each item is properly cast - }).toList(), - onChanged: - widget.isViewMode - ? null - : (Map? newItem) { - if (newItem != null) { - setState(() { - _selectedIsBillable = - newItem['dropdown_key'].toString(); - }); - } - }, ), ), ), @@ -2132,7 +2225,9 @@ class CreateNewPlansState extends State { ), 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,90 +2240,108 @@ class CreateNewPlansState extends State { child: CircularProgressIndicator(), ), ) - : DropdownSearch( - selectedItem: costCenterMap[selectedCostCenterId], - enabled: !widget.isViewMode, - popupProps: PopupProps.menu( - showSearchBox: true, - fit: FlexFit.loose, - constraints: BoxConstraints(maxHeight: 250), - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search Cost Center...", - // hintStyle: TextStyle(fontSize: 13, color: Colors.grey), - hintStyle: GoogleFonts.poppins( - fontSize: 13, - color: Colors.grey, - ), - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - ), - ), - style: GoogleFonts.poppins(fontSize: 13), - ), - menuProps: MenuProps(backgroundColor: Colors.white), - itemBuilder: - (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, - ), - child: Text( - item, - style: GoogleFonts.poppins( - fontSize: 13, - ), // 👈 Set your desired text size here - ), - ), - ), - - items: costCenterMap.values.toList(), // just names - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - // contentPadding: EdgeInsets.symmetric(horizontal: 1), - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - ), - ), - - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null || selectedItem.isEmpty) { - return Text( - "Select Purpose", // fallback text - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.grey, - ), - ); - } - return Text( - selectedItem ?? '', - style: GoogleFonts.poppins( - fontSize: 12, - color: Colors.black, - ), - ); - }, - // dropdownBuilder: (context, selectedItem) => Align( - // alignment: Alignment.centerLeft, - // child: Text( - // selectedItem ?? "Select", - // style: TextStyle(fontSize: 12), - // ), - // ), - onChanged: (String? newValue) { + : Focus( + focusNode: focusNodes["cost_center_idFocusNode"], + onFocusChange: (hasFocus) { setState(() { - selectedCostCenterId = - costCenterMap.entries - .firstWhere( - (entry) => entry.value == newValue, - ) - .key; + focusStates["cost_center_idFocused"] = hasFocus; }); }, + child: GestureDetector( + onTap: () { + // Request focus when user taps + focusNodes["cost_center_idFocusNode"] + ?.requestFocus(); + }, + child: DropdownSearch( + selectedItem: costCenterMap[selectedCostCenterId], + enabled: !widget.isViewMode, + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Cost Center...", + // hintStyle: TextStyle(fontSize: 13, color: Colors.grey), + hintStyle: GoogleFonts.poppins( + fontSize: 13, + color: Colors.grey, + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), + ), + style: GoogleFonts.poppins(fontSize: 13), + ), + menuProps: MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: GoogleFonts.poppins( + fontSize: 13, + ), // 👈 Set your desired text size here + ), + ), + ), + + items: costCenterMap.values.toList(), // just names + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + // contentPadding: EdgeInsets.symmetric(horizontal: 1), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || + selectedItem.isEmpty) { + return Text( + "Select Purpose", // fallback text + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.grey, + ), + ); + } + return Text( + selectedItem ?? '', + style: GoogleFonts.poppins( + fontSize: 12, + color: Colors.black, + ), + ); + }, + // dropdownBuilder: (context, selectedItem) => Align( + // alignment: Alignment.centerLeft, + // child: Text( + // selectedItem ?? "Select", + // style: TextStyle(fontSize: 12), + // ), + // ), + onChanged: (String? newValue) { + setState(() { + selectedCostCenterId = + costCenterMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; + }); + }, + ), + ), ), ), @@ -2286,8 +2399,9 @@ class CreateNewPlansState extends State { 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,95 +2411,117 @@ class CreateNewPlansState extends State { ? Center( child: CircularProgressIndicator(), ) // Show loading inside dropdown - : DropdownSearch>( - selectedItem: purposeList.firstWhere( - (item) => - item['dropdown_key'].toString() == - selectedPurpose, - orElse: () => {}, // ✅ Safe fallback - ), - items: purposeList, - itemAsString: - (Map item) => - item['dropdown_value'], - popupProps: PopupProps.menu( - showSearchBox: true, - fit: FlexFit.loose, - constraints: BoxConstraints(maxHeight: 250), - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search Purpose...", - hintStyle: GoogleFonts.poppins( - fontSize: 13, - color: Colors.grey, + : 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>( + selectedItem: purposeList.firstWhere( + (item) => + item['dropdown_key'].toString() == + selectedPurpose, + orElse: + () => {}, // ✅ Safe fallback + ), + items: purposeList, + itemAsString: + (Map item) => + item['dropdown_value'], + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search Purpose...", + hintStyle: GoogleFonts.poppins( + fontSize: 13, + color: Colors.grey, + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), + ), + style: GoogleFonts.poppins(fontSize: 13), ), - contentPadding: EdgeInsets.symmetric( - horizontal: 10, + menuProps: MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item['dropdown_value'], + style: GoogleFonts.poppins( + fontSize: 13, + ), // 👈 Set your desired text size here + ), + ), + ), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + border: InputBorder.none, ), ), - style: GoogleFonts.poppins(fontSize: 13), - ), - menuProps: MenuProps(backgroundColor: Colors.white), - itemBuilder: - (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, - ), - child: Text( - item['dropdown_value'], + onChanged: + widget.isViewMode + ? null + : (Map? newValue) { + setState(() { + selectedPurpose = + newValue?['dropdown_key'] + .toString(); + }); + print( + "selectedPurpose - $selectedPurpose", + ); + }, + + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || + selectedItem.isEmpty) { + return Text( + "Select Purpose", // fallback text style: GoogleFonts.poppins( fontSize: 13, - ), // 👈 Set your desired text size here + color: Colors.grey, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Small font size for selected item + color: Colors.black, ), - ), - ), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - border: InputBorder.none, + ); + }, + // dropdownBuilder: (context, selectedItem) => Align( + // alignment: Alignment.centerLeft, + // child: Text( + // selectedItem?['dropdown_value'] ?? '', + // style: TextStyle(fontSize: 12), + // ), + // ), ), ), - onChanged: - widget.isViewMode - ? null - : (Map? newValue) { - setState(() { - selectedPurpose = - newValue?['dropdown_key'].toString(); - }); - print("selectedPurpose - $selectedPurpose"); - }, - - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null || selectedItem.isEmpty) { - return Text( - "Select Purpose", // fallback text - style: GoogleFonts.poppins( - fontSize: 13, - color: Colors.grey, - ), - ); - } - return Text( - selectedItem['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Small font size for selected item - color: Colors.black, - ), - ); - }, - // dropdownBuilder: (context, selectedItem) => Align( - // alignment: Alignment.centerLeft, - // child: Text( - // selectedItem?['dropdown_value'] ?? '', - // style: TextStyle(fontSize: 12), - // ), - // ), ), ), ), @@ -2446,12 +2582,12 @@ class CreateNewPlansState extends State { ), 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,99 +2596,122 @@ class CreateNewPlansState extends State { ? Center( child: CircularProgressIndicator(), ) // Show loading inside dropdown - : DropdownSearch>( - selectedItem: funcDeptList.firstWhere( - (item) => - item['dropdown_key'].toString() == - selectedFuncDept, - orElse: () => {}, // ✅ Safe fallback - ), - items: funcDeptList, - itemAsString: - (Map item) => - item['dropdown_value'], - popupProps: PopupProps.menu( - showSearchBox: true, - fit: FlexFit.loose, - constraints: BoxConstraints(maxHeight: 250), - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search department...", - hintStyle: GoogleFonts.poppins( - fontSize: 13, - color: Colors.grey, - ), - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - ), - ), - style: GoogleFonts.poppins( - fontSize: - 13, // 👈 Small font size for selected item - ), - ), - menuProps: MenuProps(backgroundColor: Colors.white), - itemBuilder: - (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, - ), - child: Text( - item['dropdown_value'], - style: GoogleFonts.poppins( - fontSize: - 13, // 👈 Small font size for selected item - ), // 👈 Set your desired text size here - ), - ), - ), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - contentPadding: EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - border: InputBorder.none, - ), - ), - onChanged: - widget.isViewMode - ? null - : (Map? newValue) { - setState(() { - selectedFuncDept = - newValue?['dropdown_key'].toString(); - }); - print("selectedFuncDept - $selectedFuncDept"); - }, - - dropdownBuilder: (context, selectedItem) { - if (selectedItem == null || selectedItem.isEmpty) { - return const Text( - "Select Purpose", // fallback text - style: TextStyle( - fontSize: 12, - color: Colors.grey, - ), - ); - } - return Text( - selectedItem['dropdown_value'] ?? '', - style: GoogleFonts.poppins( - fontSize: - 12, // 👈 Small font size for selected item - color: Colors.black, - ), - ); + : Focus( + focusNode: focusNodes["functional_departmentFocusNode"], + onFocusChange: (hasFocus) { + setState(() { + focusStates["functional_departmentFocused"] = + hasFocus; + }); }, - // dropdownBuilder: (context, selectedItem) => Align( - // alignment: Alignment.centerLeft, - // child: Text( - // selectedItem?['dropdown_value'] ?? '', - // style: TextStyle(fontSize: 12), - // ), - // ), + child: GestureDetector( + onTap: () { + // Request focus when user taps + focusNodes["functional_departmentFocusNode"] + ?.requestFocus(); + }, + child: DropdownSearch>( + selectedItem: funcDeptList.firstWhere( + (item) => + item['dropdown_key'].toString() == + selectedFuncDept, + orElse: + () => {}, // ✅ Safe fallback + ), + items: funcDeptList, + itemAsString: + (Map item) => + item['dropdown_value'], + popupProps: PopupProps.menu( + showSearchBox: true, + fit: FlexFit.loose, + constraints: BoxConstraints(maxHeight: 250), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search department...", + hintStyle: GoogleFonts.poppins( + fontSize: 13, + color: Colors.grey, + ), + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + ), + ), + style: GoogleFonts.poppins( + fontSize: + 13, // 👈 Small font size for selected item + ), + ), + menuProps: MenuProps( + backgroundColor: Colors.white, + ), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item['dropdown_value'], + style: GoogleFonts.poppins( + fontSize: + 13, // 👈 Small font size for selected item + ), // 👈 Set your desired text size here + ), + ), + ), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + contentPadding: EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + border: InputBorder.none, + ), + ), + onChanged: + widget.isViewMode + ? null + : (Map? newValue) { + setState(() { + selectedFuncDept = + newValue?['dropdown_key'] + .toString(); + }); + print( + "selectedFuncDept - $selectedFuncDept", + ); + }, + + dropdownBuilder: (context, selectedItem) { + if (selectedItem == null || + selectedItem.isEmpty) { + return const Text( + "Select Purpose", // fallback text + style: TextStyle( + fontSize: 12, + color: Colors.grey, + ), + ); + } + return Text( + selectedItem['dropdown_value'] ?? '', + style: GoogleFonts.poppins( + fontSize: + 12, // 👈 Small font size for selected item + color: Colors.black, + ), + ); + }, + // dropdownBuilder: (context, selectedItem) => Align( + // alignment: Alignment.centerLeft, + // child: Text( + // selectedItem?['dropdown_value'] ?? '', + // style: TextStyle(fontSize: 12), + // ), + // ), + ), + ), ), ), ), @@ -2578,65 +2737,83 @@ class CreateNewPlansState extends State { return [ CustomTextFieldWrapper( - isFocused: false, + isFocused: focusStates["trip_plannedFocused"] ?? false, + isDesktop: isDesktop, layoutColor: widget.layoutColor, child: SizedBox( height: 35, width: double.infinity, - child: DropdownSearch( - selectedItem: - options.firstWhere( - (opt) => opt['value'] == _selectedOption, - )['title'], - enabled: !widget.isViewMode, - items: options.map((opt) => opt['title']!).toList(), - popupProps: PopupProps.menu( - showSearchBox: false, - menuProps: const MenuProps(backgroundColor: Colors.white), - constraints: BoxConstraints(maxHeight: 100), - itemBuilder: - (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, - ), - child: Text(item, style: GoogleFonts.poppins(fontSize: 13)), - ), - ), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 5, - ), - ), - ), - dropdownBuilder: (context, selectedItem) { - return Text( - selectedItem ?? "Select Purpose", - style: GoogleFonts.poppins( - fontSize: 12, - color: selectedItem == null ? Colors.grey : Colors.black, - ), - ); - }, - onChanged: (String? newTitle) { - if (newTitle == null) return; - final selected = options.firstWhere( - (opt) => opt["title"] == newTitle, - ); + child: Focus( + focusNode: focusNodes["trip_plannedFocusNode"], + onFocusChange: (hasFocus) { setState(() { - _selectedOption = selected["value"]!; - if (_selectedOption == "Option 2" || - _selectedOption == "Option 3") { - _showInputDialog(selected["title"]!); - } else if (_selectedOption == "Option 1") { - otherUserName = userName; - } + focusStates["trip_plannedFocused"] = hasFocus; }); }, + child: GestureDetector( + onTap: () { + // Request focus when user taps + focusNodes["trip_plannedFocusNode"]?.requestFocus(); + }, + child: DropdownSearch( + selectedItem: + options.firstWhere( + (opt) => opt['value'] == _selectedOption, + )['title'], + enabled: !widget.isViewMode, + items: options.map((opt) => opt['title']!).toList(), + popupProps: PopupProps.menu( + showSearchBox: false, + menuProps: const MenuProps(backgroundColor: Colors.white), + constraints: BoxConstraints(maxHeight: 100), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 13), + ), + ), + ), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 5, + ), + ), + ), + dropdownBuilder: (context, selectedItem) { + return Text( + selectedItem ?? "Select Purpose", + style: GoogleFonts.poppins( + fontSize: 12, + color: selectedItem == null ? Colors.grey : Colors.black, + ), + ); + }, + onChanged: (String? newTitle) { + if (newTitle == null) return; + final selected = options.firstWhere( + (opt) => opt["title"] == newTitle, + ); + setState(() { + _selectedOption = selected["value"]!; + if (_selectedOption == "Option 2" || + _selectedOption == "Option 3") { + _showInputDialog(selected["title"]!); + } else if (_selectedOption == "Option 1") { + otherUserName = userName; + } + }); + }, + ), + ), ), ), ), @@ -2892,12 +3069,13 @@ class CreateNewPlansState extends State { ), 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 { // ), // ), 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 { 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 { 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( diff --git a/lib/widgets/custom_text_field.dart b/lib/widgets/custom_text_field.dart index 8254c07..1aeed1c 100644 --- a/lib/widgets/custom_text_field.dart +++ b/lib/widgets/custom_text_field.dart @@ -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 { + 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 { @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 // ? [ diff --git a/lib/widgets/custom_text_itnerary_sub.dart b/lib/widgets/custom_text_itnerary_sub.dart index 9fe0251..efe58ac 100644 --- a/lib/widgets/custom_text_itnerary_sub.dart +++ b/lib/widgets/custom_text_itnerary_sub.dart @@ -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,36 +23,64 @@ class CustomTextFieldItnerarySubWrapper extends StatefulWidget { }); @override - _CustomTextFieldItnerarySubWrapperState createState() => _CustomTextFieldItnerarySubWrapperState(); + _CustomTextFieldItnerarySubWrapperState createState() => + _CustomTextFieldItnerarySubWrapperState(); } -class _CustomTextFieldItnerarySubWrapperState extends State { +class _CustomTextFieldItnerarySubWrapperState + extends State { + 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), - border: Border.all( - color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6), - width: widget.isFocused ? 2.0 : 0.5, - ), - boxShadow: widget.isFocused - ? [ - BoxShadow( - color: Color.fromRGBO(120, 180, 252, 0.3), - blurRadius: 10, - spreadRadius: 2, - offset: Offset(0, 4), + color: Colors.white, - ), - ] - : [], + border: Border.all( + color: widget.isFocused ? layoutColor! : Color(0xFFD6D5E6), + // color: widget.isFocused ? Color(0xFF78B4FC) : Color(0xFFD6D5E6), + width: widget.isFocused ? 1.0 : 0.5, + ), + boxShadow: + widget.isFocused + ? [ + BoxShadow( + color: Colors.white, + // color: Color.fromRGBO(120, 180, 252, 0.3), + blurRadius: 10, + spreadRadius: 2, + offset: Offset(0, 4), + ), + ] + : [], ), child: widget.child, );