diff --git a/lib/Screens/group/group.dart b/lib/Screens/group/group.dart index 85ebdf2..88a28f6 100644 --- a/lib/Screens/group/group.dart +++ b/lib/Screens/group/group.dart @@ -51,7 +51,10 @@ class _groupState extends State { List? apiForDomestic; List? apiForInternational; Map errorMessages = {}; + final Map controllers = {}; + Map focusNodes = {}; + Map focusStates = {}; List dataHeader = ["name", "description"]; @@ -82,7 +85,14 @@ class _groupState extends State { for (var field in dataHeader) { controllers[field] = TextEditingController(); + focusNodes["${field}FocusNode"] = FocusNode(); + focusStates["${field}Focused"] = false; } + + print("Focus Nodes KeysII: ${focusNodes.keys.toList()}"); + print("Focus States KeysII: ${focusStates.keys.toList()}"); + print("Text Controllers KeysII: ${controllers.keys.toList()}"); + updateData(); loadInitialData(); }); diff --git a/lib/Screens/group/groupDetails.dart b/lib/Screens/group/groupDetails.dart index 9f0cb19..916b54e 100644 --- a/lib/Screens/group/groupDetails.dart +++ b/lib/Screens/group/groupDetails.dart @@ -45,6 +45,9 @@ class GroupDataState extends State { Map? apiData; final Map controllers = {}; + Map focusNodes = {}; + Map focusStates = {}; + Map errorMessages = {}; List domesticList = []; @@ -97,8 +100,26 @@ class GroupDataState extends State { apiForInternational = null; apiData = null; + // for (var field in dataHeader) { + // controllers[field] = TextEditingController(); + // } + for (var field in dataHeader) { controllers[field] = TextEditingController(); + focusNodes["${field}FocusNode"] = FocusNode(); + focusStates["${field}Focused"] = false; + } + + print("Focus Nodes KeysII: ${focusNodes.keys.toList()}"); + print("Focus States KeysII: ${focusStates.keys.toList()}"); + print("Text Controllers KeysII: ${controllers.keys.toList()}"); + + for (var key in focusNodes.keys) { + _addFocusListener(focusNodes[key]!, (focus) { + setState(() { + focusStates[key.replaceFirst("FocusNode", "Focused")] = focus; + }); + }); } if (widget.groupId != null) { @@ -116,12 +137,24 @@ class GroupDataState extends State { @override void dispose() { + for (var node in focusNodes.values) { + node.dispose(); + } + for (var controller in controllers.values) { controller.dispose(); } super.dispose(); } + void _addFocusListener(FocusNode node, Function(bool) updateState) { + node.addListener(() { + setState(() { + updateState(node.hasFocus); + }); + }); + } + void updateGroupDetails() { print("Update - ${widget.groupData}"); @@ -342,12 +375,16 @@ class GroupDataState extends State { ), SizedBox(height: 5), CustomTextFieldForexWrapper( - isFocused: false, + // isFocused: false, + isFocused: focusStates["nameFocused"] ?? false, + isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( height: 40, child: TextField( + focusNode: focusNodes["nameFocusNode"], + controller: controllers["name"], style: const TextStyle(fontSize: 12), decoration: const InputDecoration( @@ -383,62 +420,87 @@ class GroupDataState extends State { ), SizedBox(height: 5), CustomTextFieldForexWrapper( - isFocused: false, + isFocused: + focusStates["international_policy_nameFocused"] ?? false, + isDesktop: widget.isDesktop, child: SizedBox( height: 40, - child: DropdownSearch( - selectedItem: - InternationalMap[selectedInternationalPolicyID], - popupProps: PopupProps.menu( - showSearchBox: true, // Enables search functionality - menuProps: const MenuProps(backgroundColor: Colors.white), - constraints: BoxConstraints(maxHeight: 250), - itemBuilder: - (context, item, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, - ), - child: Text( - item, - style: GoogleFonts.poppins(fontSize: 11.5), - ), - ), - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search ...", - hintStyle: GoogleFonts.poppins(fontSize: 11), - contentPadding: EdgeInsets.symmetric(horizontal: 4), - ), - ), - ), - items: InternationalMap.values.toList(), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 1), - ), - ), - dropdownBuilder: - (context, selectedItem) => Align( - // Center-align selected item - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select", - style: GoogleFonts.poppins(fontSize: 11), - ), - ), - onChanged: (String? newValue) { + child: Focus( + focusNode: focusNodes["international_policy_nameFocusNode"], + onFocusChange: (hasFocus) { setState(() { - // Find the country_code based on selected country_name - selectedInternationalPolicyID = - InternationalMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; - selectedInternationalPolicyName = newValue; + focusStates["international_policy_nameFocused"] = + hasFocus; }); }, + child: GestureDetector( + onTap: () { + // Request focus when user taps + focusNodes["international_policy_nameFocusNode"] + ?.requestFocus(); + }, + child: DropdownSearch( + selectedItem: + InternationalMap[selectedInternationalPolicyID], + popupProps: PopupProps.menu( + showSearchBox: true, // Enables search functionality + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + constraints: BoxConstraints(maxHeight: 250), + itemBuilder: + (context, item, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + item, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search ...", + hintStyle: GoogleFonts.poppins(fontSize: 11), + contentPadding: EdgeInsets.symmetric( + horizontal: 4, + ), + ), + ), + ), + items: InternationalMap.values.toList(), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + // Center-align selected item + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + selectedInternationalPolicyID = + InternationalMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; + selectedInternationalPolicyName = newValue; + }); + }, + ), + ), ), ), ), @@ -458,61 +520,83 @@ class GroupDataState extends State { ), SizedBox(height: 5), CustomTextFieldForexWrapper( - isFocused: false, + isFocused: focusStates["domestic_policy_nameFocused"] ?? false, isDesktop: widget.isDesktop, child: SizedBox( height: 40, - child: DropdownSearch( - selectedItem: DomesticMap[selectedDomesticPolicyID], - popupProps: PopupProps.menu( - showSearchBox: true, // Enables search functionality - menuProps: const MenuProps(backgroundColor: Colors.white), - constraints: BoxConstraints(maxHeight: 250), - itemBuilder: - (context, object, isSelected) => Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 6.0, - ), - child: Text( - object, - style: GoogleFonts.poppins(fontSize: 11.5), - ), - ), - searchFieldProps: TextFieldProps( - decoration: InputDecoration( - hintText: "Search ...", - hintStyle: GoogleFonts.poppins(fontSize: 11), - contentPadding: EdgeInsets.symmetric(horizontal: 4), - ), - ), - ), - items: DomesticMap.values.toList(), - dropdownDecoratorProps: DropDownDecoratorProps( - dropdownSearchDecoration: InputDecoration( - border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: 1), - ), - ), - dropdownBuilder: - (context, selectedItem) => Align( - // Center-align selected item - alignment: Alignment.centerLeft, - child: Text( - selectedItem ?? "Select ", - style: GoogleFonts.poppins(fontSize: 11), - ), - ), - onChanged: (String? newValue) { + child: Focus( + focusNode: focusNodes["domestic_policy_nameFocusNode"], + onFocusChange: (hasFocus) { setState(() { - // Find the country_code based on selected country_name - selectedDomesticPolicyID = - DomesticMap.entries - .firstWhere((entry) => entry.value == newValue) - .key; - selectedDomesticPolicyName = newValue; + focusStates["domestic_policy_nameFocused"] = hasFocus; }); }, + child: GestureDetector( + // + onTap: () { + // Request focus when user taps + focusNodes["domestic_policy_nameFocusNode"] + ?.requestFocus(); + }, + child: DropdownSearch( + selectedItem: DomesticMap[selectedDomesticPolicyID], + popupProps: PopupProps.menu( + showSearchBox: true, // Enables search functionality + menuProps: const MenuProps( + backgroundColor: Colors.white, + ), + constraints: BoxConstraints(maxHeight: 250), + itemBuilder: + (context, object, isSelected) => Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 6.0, + ), + child: Text( + object, + style: GoogleFonts.poppins(fontSize: 11.5), + ), + ), + searchFieldProps: TextFieldProps( + decoration: InputDecoration( + hintText: "Search ...", + hintStyle: GoogleFonts.poppins(fontSize: 11), + contentPadding: EdgeInsets.symmetric( + horizontal: 4, + ), + ), + ), + ), + items: DomesticMap.values.toList(), + dropdownDecoratorProps: DropDownDecoratorProps( + dropdownSearchDecoration: InputDecoration( + border: InputBorder.none, + contentPadding: EdgeInsets.symmetric(horizontal: 1), + ), + ), + dropdownBuilder: + (context, selectedItem) => Align( + // Center-align selected item + alignment: Alignment.centerLeft, + child: Text( + selectedItem ?? "Select ", + style: GoogleFonts.poppins(fontSize: 11), + ), + ), + onChanged: (String? newValue) { + setState(() { + // Find the country_code based on selected country_name + selectedDomesticPolicyID = + DomesticMap.entries + .firstWhere( + (entry) => entry.value == newValue, + ) + .key; + selectedDomesticPolicyName = newValue; + }); + }, + ), + ), ), ), ), @@ -532,13 +616,16 @@ class GroupDataState extends State { ), SizedBox(height: 5), CustomTextFieldForexWrapper( - isFocused: false, + isFocused: focusStates["descriptionFocused"] ?? false, + isDesktop: widget.isDesktop, color: Colors.transparent, child: SizedBox( height: 100, child: TextField( controller: controllers["description"], + focusNode: focusNodes["descriptionFocusNode"], + style: const TextStyle(fontSize: 12), maxLines: null, expands: true, diff --git a/lib/routes/custom_drawer.dart b/lib/routes/custom_drawer.dart index 16bd73f..245d721 100644 --- a/lib/routes/custom_drawer.dart +++ b/lib/routes/custom_drawer.dart @@ -6,6 +6,7 @@ import 'package:google_fonts/google_fonts.dart'; import 'package:responsive_builder/responsive_builder.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import '../config/apiUrl.dart'; import '../services/apiService.dart'; class CustomDrawer extends StatefulWidget { @@ -116,7 +117,10 @@ class _CustomDrawerState extends State { String? rawLogoPath = selectedOrg?['logo']; if (rawLogoPath != null && rawLogoPath.contains('/assets')) { - const baseUrl = "https://apitest.tripapprovaltool.com"; + // const baseUrl = "https://apitest.tripapprovaltool.com"; + + const baseUrl = apiUrl; + final assetPath = rawLogoPath.split('/assets').last; selectedOrg!['logo'] = "$baseUrl/assets$assetPath"; } @@ -139,13 +143,15 @@ class _CustomDrawerState extends State { @override Widget build(BuildContext context) { Widget drawerContent = Container( - // color: Colors.white, + color: Colors.white, child: Container( + color: Colors.white, margin: const EdgeInsets.all(18), child: Column( children: [ Container( - margin: const EdgeInsets.only(left: 20), + color: Colors.white, + // margin: const EdgeInsets.only(left: 20), child: Column( children: [ Row( @@ -156,7 +162,7 @@ class _CustomDrawerState extends State { ? ClipRect( child: Image.network( selectedOrg!['logo'], - width: 100, + width: 50, height: 50, fit: BoxFit.contain, errorBuilder: (context, error, stackTrace) { @@ -197,7 +203,7 @@ class _CustomDrawerState extends State { userData?["role"] == "Travel Admin") _buildDrawerItem( context, - Icons.insights_outlined, + Icons.format_list_bulleted_rounded, 'All Trips', '/listAllPlan', ), @@ -205,15 +211,15 @@ class _CustomDrawerState extends State { if (userDetails["role"] == "Travel Agent") _buildDrawerItem( context, - Icons.assessment_outlined, - 'My Approvals', + Icons.shopping_bag_outlined, + 'Trips', '/listTravelAgentPlan', ), if (userDetails["role"] != "Travel Agent") _buildDrawerItem( context, - Icons.request_page_outlined, + Icons.shopping_bag_outlined, 'My Trips', '/listPlan', ), @@ -221,38 +227,42 @@ class _CustomDrawerState extends State { if (userDetails["role"] != "Travel Agent") _buildDrawerItem( context, - Icons.assessment_outlined, + Icons.verified_outlined, 'My Approvals', '/ApprovalList', ), - - SizedBox(height: MediaQuery.of(context).size.height * 0.5), + // Spacer(), + SizedBox(height: MediaQuery.of(context).size.height / 2), Container( - margin: const EdgeInsets.all(20), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - "Powered by", - style: TextStyle( - fontSize: 11, - color: Color(0xFF212121), + margin: const EdgeInsets.only(right: 20), + child: Align( + alignment: Alignment.center, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Column( + // mainAxisAlignment: MainAxisAlignment.center, + // crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "Powered by", + style: GoogleFonts.poppins( + fontSize: 11, + color: Colors.grey, + // color: Color(0xFF212121), + ), ), - ), - Image.asset( - 'assets/images/login/logoNew.jpg', - width: 90, - height: 25, - fit: BoxFit.contain, - ), - ], - ), - ], + Image.asset( + 'assets/images/login/logoNew.jpg', + // width: 90, + height: 45, + fit: BoxFit.contain, + ), + ], + ), + ], + ), ), ), ], @@ -260,8 +270,16 @@ class _CustomDrawerState extends State { ), ); - return Drawer( - child: ListView(padding: EdgeInsets.zero, children: [drawerContent]), + return Container( + height: MediaQuery.of(context).size.height, + child: Drawer( + backgroundColor: Colors.white, + child: ListView( + physics: NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + children: [drawerContent], + ), + ), ); // if (widget.isDesktop) { diff --git a/lib/widgets/custom_text_forex.dart b/lib/widgets/custom_text_forex.dart index 289217d..4121aba 100644 --- a/lib/widgets/custom_text_forex.dart +++ b/lib/widgets/custom_text_forex.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import '../utils/auth_utils.dart'; + class CustomTextFieldForexWrapper extends StatefulWidget { final Widget child; final bool isFocused; @@ -27,33 +29,79 @@ class CustomTextFieldForexWrapper extends StatefulWidget { class _CustomTextFieldForexWrapperState 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.2 : MediaQuery.of(context).size.width * 0.8), padding: widget.padding, decoration: BoxDecoration( - color: widget.color, - // color: Color(0xFFF7F7FB), + // color: widget.color, + color: Colors.white, borderRadius: BorderRadius.circular(10), 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( - color: Color.fromRGBO(120, 180, 252, 0.3), - blurRadius: 10, - spreadRadius: 2, - offset: Offset(0, 4), - ), - ] - : [], + boxShadow: + widget.isFocused + ? [ + BoxShadow( + color: Colors.white, + // color: Color.fromRGBO(120, 180, 252, 0.3), + // color: Color.fromRGBO(120, 180, 252, 0.3), + blurRadius: 5, + spreadRadius: 2, + offset: Offset(0, 1), + ), + ] + : [], ), + // decoration: BoxDecoration( + // color: widget.color, + // // color: Color(0xFFF7F7FB), + // 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), + // ), + // ] + // : [], + // ), child: widget.child, ); }