From 08991f20956f2f021c4cae5c57cfc4001f131ffd Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Sat, 31 May 2025 10:43:39 +0530 Subject: [PATCH 1/4] refactor: Improve LoginController and LoginScreen structure and readability --- lib/controller/auth/login_controller.dart | 64 ++-- lib/view/auth/login_screen.dart | 381 +++++++++++----------- 2 files changed, 228 insertions(+), 217 deletions(-) diff --git a/lib/controller/auth/login_controller.dart b/lib/controller/auth/login_controller.dart index 9a18200..7478bd4 100644 --- a/lib/controller/auth/login_controller.dart +++ b/lib/controller/auth/login_controller.dart @@ -6,51 +6,65 @@ import 'package:marco/helpers/widgets/my_form_validator.dart'; import 'package:marco/helpers/widgets/my_validators.dart'; class LoginController extends MyController { - MyFormValidator basicValidator = MyFormValidator(); + // Form validator + final MyFormValidator basicValidator = MyFormValidator(); - bool showPassword = false, isChecked = false; - RxBool isLoading = false.obs; // Add reactive loading state + // UI states + final RxBool isLoading = false.obs; + final RxBool showPassword = false.obs; + final RxBool isChecked = false.obs; + // Dummy credentials final String _dummyEmail = "admin@marcoaiot.com"; final String _dummyPassword = "User@123"; @override void onInit() { - basicValidator.addField('username', required: true, label: "User_Name", validators: [MyEmailValidator()], controller: TextEditingController(text: _dummyEmail)); - basicValidator.addField('password', required: true, label: "Password", validators: [MyLengthValidator(min: 6, max: 10)], controller: TextEditingController(text: _dummyPassword)); super.onInit(); + + basicValidator.addField( + 'username', + required: true, + label: "User_Name", + validators: [MyEmailValidator()], + controller: TextEditingController(text: _dummyEmail), + ); + + basicValidator.addField( + 'password', + required: true, + label: "Password", + validators: [MyLengthValidator(min: 6, max: 10)], + controller: TextEditingController(text: _dummyPassword), + ); } void onChangeCheckBox(bool? value) { - isChecked = value ?? isChecked; - update(); + isChecked.value = value ?? isChecked.value; } void onChangeShowPassword() { - showPassword = !showPassword; - update(); + showPassword.toggle(); } Future onLogin() async { - if (basicValidator.validateForm()) { - // Set loading to true - isLoading.value = true; - update(); + if (!basicValidator.validateForm()) return; - var errors = await AuthService.loginUser(basicValidator.getData()); - if (errors != null) { - basicValidator.addErrors(errors); - basicValidator.validateForm(); - basicValidator.clearErrors(); - } else { - String nextUrl = Uri.parse(ModalRoute.of(Get.context!)?.settings.name ?? "").queryParameters['next'] ?? "/home"; - Get.toNamed(nextUrl); - } + isLoading.value = true; - // Set loading to false after the API call is complete - isLoading.value = false; - update(); + final errors = await AuthService.loginUser(basicValidator.getData()); + + if (errors != null) { + basicValidator.addErrors(errors); + basicValidator.validateForm(); + basicValidator.clearErrors(); + } else { + final currentRoute = ModalRoute.of(Get.context!)?.settings.name ?? ""; + final nextUrl = Uri.parse(currentRoute).queryParameters['next'] ?? "/home"; + Get.toNamed(nextUrl); } + + isLoading.value = false; } void goToForgotPassword() { diff --git a/lib/view/auth/login_screen.dart b/lib/view/auth/login_screen.dart index 2ad8a6b..c3c6bb5 100644 --- a/lib/view/auth/login_screen.dart +++ b/lib/view/auth/login_screen.dart @@ -20,11 +20,11 @@ class LoginScreen extends StatefulWidget { } class _LoginScreenState extends State with UIMixin { - late LoginController controller; + late final LoginController controller; @override void initState() { - controller = Get.put(LoginController()); + controller = Get.put(LoginController(), tag: 'login_controller'); super.initState(); } @@ -32,204 +32,201 @@ class _LoginScreenState extends State with UIMixin { Widget build(BuildContext context) { return AuthLayout( child: GetBuilder( - init: controller, tag: 'login_controller', - builder: (controller) { + builder: (_) { return Obx(() { - return controller.isLoading.value - ? Center(child: CircularProgressIndicator()) // Show loading spinner when isLoading is true - : Form( - key: controller.basicValidator.formKey, - child: SingleChildScrollView( - padding: MySpacing.xy(2, 40), - child: Container( - width: double.infinity, - padding: MySpacing.all(24), - decoration: BoxDecoration( - color: theme.colorScheme.primary.withOpacity(0.02), - borderRadius: BorderRadius.circular(8), - border: Border.all( - color: contentTheme.primary.withOpacity(0.5), - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - /// Logo - Center( - child: Image.asset( - Images.logoDark, - height: 120, - fit: BoxFit.contain, - ), - ), - MySpacing.height(20), + if (controller.isLoading.value) { + return const Center(child: CircularProgressIndicator()); + } - /// Welcome Text - Center( - child: MyText.bodyLarge("Welcome Back!", fontWeight: 600), - ), - MySpacing.height(4), - Center( - child: MyText.bodySmall("Please sign in to continue."), - ), - MySpacing.height(20), - - /// Email Field - MyText.bodySmall("Email Address", fontWeight: 600), - MySpacing.height(8), - Material( - elevation: 2, - shadowColor: contentTheme.secondary.withAlpha(30), - borderRadius: BorderRadius.circular(12), - child: TextFormField( - validator: - controller.basicValidator.getValidation('username'), - controller: - controller.basicValidator.getController('username'), - keyboardType: TextInputType.emailAddress, - style: MyTextStyle.labelMedium(), - decoration: InputDecoration( - hintText: "Enter your email", - hintStyle: MyTextStyle.bodySmall(xMuted: true), - filled: true, - fillColor: theme.cardColor, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(2), - borderSide: BorderSide.none, - ), - prefixIcon: const Icon(LucideIcons.mail, size: 18), - contentPadding: MySpacing.xy(12, 16), - ), - ), - ), - MySpacing.height(16), - - /// Password Field Label - MyText.bodySmall("Password", fontWeight: 600), - MySpacing.height(8), - Material( - elevation: 2, - shadowColor: contentTheme.secondary.withAlpha(25), - borderRadius: BorderRadius.circular(12), - child: TextFormField( - validator: - controller.basicValidator.getValidation('password'), - controller: - controller.basicValidator.getController('password'), - keyboardType: TextInputType.visiblePassword, - obscureText: !controller.showPassword, - style: MyTextStyle.labelMedium(), - decoration: InputDecoration( - hintText: "Enter your password", - hintStyle: MyTextStyle.bodySmall(xMuted: true), - filled: true, - fillColor: theme.cardColor, - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(2), - borderSide: BorderSide.none, - ), - prefixIcon: const Icon(LucideIcons.lock, size: 18), - suffixIcon: InkWell( - onTap: controller.onChangeShowPassword, - child: Icon( - controller.showPassword - ? LucideIcons.eye - : LucideIcons.eye_off, - size: 18, - ), - ), - contentPadding: MySpacing.all(3), - ), - ), - ), - - MySpacing.height(16), - - /// Remember Me + Forgot Password - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - InkWell( - onTap: () => controller - .onChangeCheckBox(!controller.isChecked), - child: Row( - children: [ - Checkbox( - onChanged: controller.onChangeCheckBox, - value: controller.isChecked, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(4), - ), - fillColor: WidgetStatePropertyAll( - contentTheme.secondary), - checkColor: contentTheme.onPrimary, - visualDensity: getCompactDensity, - materialTapTargetSize: - MaterialTapTargetSize.shrinkWrap, - ), - MySpacing.width(8), - MyText.bodySmall("Remember Me"), - ], - ), - ), - MyButton.text( - onPressed: controller.goToForgotPassword, - elevation: 0, - padding: MySpacing.xy(8, 0), - splashColor: contentTheme.secondary.withAlpha(36), - child: MyText.bodySmall( - 'Forgot password?', - fontWeight: 600, - color: contentTheme.secondary, - ), - ), - ], - ), - MySpacing.height(28), - - /// Login Button - Center( - child: MyButton.rounded( - onPressed: controller.onLogin, - elevation: 2, - padding: MySpacing.xy(24, 16), - borderRadiusAll: 16, - backgroundColor: Colors.blueAccent, - child: MyText.labelMedium( - 'Login', - fontWeight: 600, - color: contentTheme.onPrimary, - ), - ), - ), - MySpacing.height(16), - - /// Register Link - Center( - child: MyButton.text( - onPressed: () { - OrganizationFormBottomSheet.show(context); - }, - elevation: 0, - padding: MySpacing.xy(12, 8), - splashColor: - contentTheme.secondary.withAlpha(30), - child: MyText.bodySmall( - "Request a Demo", - color: contentTheme.secondary, - fontWeight: 600, - ), - ), - ), - ], + return Form( + key: controller.basicValidator.formKey, + child: SingleChildScrollView( + padding: MySpacing.xy(2, 40), + child: Container( + width: double.infinity, + padding: MySpacing.all(24), + decoration: BoxDecoration( + color: theme.colorScheme.primary.withOpacity(0.02), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: contentTheme.primary.withOpacity(0.5), + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + /// Logo + Center( + child: Image.asset( + Images.logoDark, + height: 120, + fit: BoxFit.contain, ), ), - ), - ); + MySpacing.height(20), + + /// Welcome + Center(child: MyText.bodyLarge("Welcome Back!", fontWeight: 600)), + MySpacing.height(4), + Center(child: MyText.bodySmall("Please sign in to continue.")), + MySpacing.height(20), + + /// Email + MyText.bodySmall("Email Address", fontWeight: 600), + MySpacing.height(8), + _buildInputField( + controller.basicValidator.getController('username')!, + controller.basicValidator.getValidation('username'), + hintText: "Enter your email", + icon: LucideIcons.mail, + keyboardType: TextInputType.emailAddress, + ), + MySpacing.height(16), + + /// Password + MyText.bodySmall("Password", fontWeight: 600), + MySpacing.height(8), + Obx(() { + return _buildInputField( + controller.basicValidator.getController('password')!, + controller.basicValidator.getValidation('password'), + hintText: "Enter your password", + icon: LucideIcons.lock, + obscureText: !controller.showPassword.value, + suffix: IconButton( + icon: Icon( + controller.showPassword.value + ? LucideIcons.eye + : LucideIcons.eye_off, + size: 18, + ), + onPressed: controller.onChangeShowPassword, + ), + ); + }), + MySpacing.height(16), + + /// Remember me + Forgot password + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Obx(() { + return InkWell( + onTap: () => + controller.onChangeCheckBox(!controller.isChecked.value), + child: Row( + children: [ + Checkbox( + value: controller.isChecked.value, + onChanged: controller.onChangeCheckBox, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + fillColor: WidgetStatePropertyAll(contentTheme.secondary), + checkColor: contentTheme.onPrimary, + visualDensity: getCompactDensity, + materialTapTargetSize: + MaterialTapTargetSize.shrinkWrap, + ), + MySpacing.width(8), + MyText.bodySmall("Remember Me"), + ], + ), + ); + }), + MyButton.text( + onPressed: controller.goToForgotPassword, + elevation: 0, + padding: MySpacing.xy(8, 0), + splashColor: contentTheme.secondary.withAlpha(36), + child: MyText.bodySmall( + 'Forgot password?', + fontWeight: 600, + color: contentTheme.secondary, + ), + ), + ], + ), + MySpacing.height(28), + + /// Login button + Center( + child: MyButton.rounded( + onPressed: controller.onLogin, + elevation: 2, + padding: MySpacing.xy(24, 16), + borderRadiusAll: 16, + backgroundColor: Colors.blueAccent, + child: MyText.labelMedium( + 'Login', + fontWeight: 600, + color: contentTheme.onPrimary, + ), + ), + ), + MySpacing.height(16), + + /// Request demo + Center( + child: MyButton.text( + onPressed: () { + OrganizationFormBottomSheet.show(context); + }, + elevation: 0, + padding: MySpacing.xy(12, 8), + splashColor: contentTheme.secondary.withAlpha(30), + child: MyText.bodySmall( + "Request a Demo", + color: contentTheme.secondary, + fontWeight: 600, + ), + ), + ), + ], + ), + ), + ), + ); }); }, ), ); } + + Widget _buildInputField( + TextEditingController controller, + FormFieldValidator? validator, { + required String hintText, + required IconData icon, + TextInputType keyboardType = TextInputType.text, + bool obscureText = false, + Widget? suffix, + }) { + return Material( + elevation: 2, + shadowColor: contentTheme.secondary.withAlpha(30), + borderRadius: BorderRadius.circular(12), + child: TextFormField( + controller: controller, + validator: validator, + obscureText: obscureText, + keyboardType: keyboardType, + style: MyTextStyle.labelMedium(), + decoration: InputDecoration( + hintText: hintText, + hintStyle: MyTextStyle.bodySmall(xMuted: true), + filled: true, + fillColor: theme.cardColor, + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(2), + borderSide: BorderSide.none, + ), + prefixIcon: Icon(icon, size: 18), + suffixIcon: suffix, + contentPadding: MySpacing.xy(12, 16), + ), + ), + ); + } } -- 2.43.0 From 9ad8bdc8936040868f1b5994caa2049650a80adf Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Sat, 31 May 2025 12:02:01 +0530 Subject: [PATCH 2/4] fix: Update button onPressed to null and adjust background color in Layout --- lib/view/layouts/layout.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/view/layouts/layout.dart b/lib/view/layouts/layout.dart index 3b4583d..b6d3601 100644 --- a/lib/view/layouts/layout.dart +++ b/lib/view/layouts/layout.dart @@ -255,12 +255,12 @@ class Layout extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ MyButton( - onPressed: () => {}, + onPressed:null, tapTargetSize: MaterialTapTargetSize.shrinkWrap, borderRadiusAll: AppStyle.buttonRadius.medium, padding: MySpacing.xy(8, 4), splashColor: contentTheme.onBackground.withAlpha(20), - backgroundColor: Colors.transparent, + backgroundColor: const Color.fromARGB(0, 220, 218, 218), child: Row( children: [ Icon( @@ -279,11 +279,11 @@ class Layout extends StatelessWidget { MySpacing.height(4), MyButton( tapTargetSize: MaterialTapTargetSize.shrinkWrap, - onPressed: () => {}, + onPressed:null, borderRadiusAll: AppStyle.buttonRadius.medium, padding: MySpacing.xy(8, 4), splashColor: contentTheme.onBackground.withAlpha(20), - backgroundColor: Colors.transparent, + backgroundColor: const Color.fromARGB(0, 220, 218, 218), child: Row( children: [ Icon( -- 2.43.0 From 706726c787517bfae16f097ebb8eea837a519fb4 Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Sat, 31 May 2025 13:08:30 +0530 Subject: [PATCH 3/4] refactor: Convert AttendanceFilterBottomSheet to StatefulWidget and enhance state management --- .../attendance/attendence_filter_sheet.dart | 400 +++++++++--------- 1 file changed, 210 insertions(+), 190 deletions(-) diff --git a/lib/model/attendance/attendence_filter_sheet.dart b/lib/model/attendance/attendence_filter_sheet.dart index d145918..6d6ea57 100644 --- a/lib/model/attendance/attendence_filter_sheet.dart +++ b/lib/model/attendance/attendence_filter_sheet.dart @@ -5,7 +5,7 @@ import 'package:intl/intl.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/utils/permission_constants.dart'; -class AttendanceFilterBottomSheet extends StatelessWidget { +class AttendanceFilterBottomSheet extends StatefulWidget { final AttendanceController controller; final PermissionController permissionController; final String selectedTab; @@ -17,35 +17,93 @@ class AttendanceFilterBottomSheet extends StatelessWidget { required this.selectedTab, }); + @override + _AttendanceFilterBottomSheetState createState() => + _AttendanceFilterBottomSheetState(); +} + +class _AttendanceFilterBottomSheetState + extends State { + late String? tempSelectedProjectId; + late String tempSelectedTab; + bool showProjectList = false; + + @override + void initState() { + super.initState(); + tempSelectedProjectId = widget.controller.selectedProjectId; + tempSelectedTab = widget.selectedTab; + } + String getLabelText() { - final startDate = controller.startDateAttendance; - final endDate = controller.endDateAttendance; + final startDate = widget.controller.startDateAttendance; + final endDate = widget.controller.endDateAttendance; if (startDate != null && endDate != null) { - final start = DateFormat('dd MM yyyy').format(startDate); - final end = DateFormat('dd MM yyyy').format(endDate); + final start = DateFormat('dd/MM/yyyy').format(startDate); + final end = DateFormat('dd/MM/yyyy').format(endDate); return "$start - $end"; } return "Date Range"; } - @override - Widget build(BuildContext context) { - String? tempSelectedProjectId = controller.selectedProjectId; - String tempSelectedTab = selectedTab; - bool showProjectList = false; - - final accessibleProjects = controller.projects + List buildProjectList() { + final accessibleProjects = widget.controller.projects .where((project) => - permissionController.isUserAssignedToProject(project.id.toString())) + widget.permissionController.isUserAssignedToProject( + project.id.toString())) .toList(); - bool hasRegularizationPermission = - permissionController.hasPermission(Permissions.regularizeAttendance); + if (accessibleProjects.isEmpty) { + return [ + const Padding( + padding: EdgeInsets.all(12.0), + child: Center(child: Text('No Projects Assigned')), + ), + ]; + } + + return accessibleProjects.map((project) { + final isSelected = tempSelectedProjectId == project.id.toString(); + return ListTile( + dense: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + title: Text(project.name), + trailing: isSelected ? const Icon(Icons.check) : null, + onTap: () { + setState(() { + tempSelectedProjectId = project.id.toString(); + showProjectList = false; + }); + }, + ); + }).toList(); + } + + List buildMainFilters() { + final accessibleProjects = widget.controller.projects + .where((project) => + widget.permissionController.isUserAssignedToProject( + project.id.toString())) + .toList(); + + final selectedProject = accessibleProjects.isNotEmpty + ? accessibleProjects.firstWhere( + (p) => p.id.toString() == tempSelectedProjectId, + orElse: () => accessibleProjects[0], + ) + : null; + + final selectedProjectName = selectedProject?.name ?? "Select Project"; + + final hasRegularizationPermission = widget.permissionController + .hasPermission(Permissions.regularizeAttendance); + final viewOptions = [ {'label': 'Today\'s Attendance', 'value': 'todaysAttendance'}, {'label': 'Attendance Logs', 'value': 'attendanceLogs'}, {'label': 'Regularization Requests', 'value': 'regularizationRequests'}, ]; + final filteredViewOptions = viewOptions.where((item) { if (item['value'] == 'regularizationRequests') { return hasRegularizationPermission; @@ -53,190 +111,152 @@ class AttendanceFilterBottomSheet extends StatelessWidget { return true; }).toList(); - return StatefulBuilder(builder: (context, setState) { - List filterWidgets; + List widgets = [ + Padding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), + child: Align( + alignment: Alignment.centerLeft, + child: MyText.titleSmall( + "Project", + fontWeight: 600, + ), + ), + ), + ListTile( + dense: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 16), + title: Text(selectedProjectName), + trailing: const Icon(Icons.arrow_drop_down), + onTap: () => setState(() => showProjectList = true), + ), + const Divider(), + Padding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), + child: Align( + alignment: Alignment.centerLeft, + child: MyText.titleSmall( + "View", + fontWeight: 600, + ), + ), + ), + ...filteredViewOptions.map((item) { + return RadioListTile( + dense: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 12), + title: Text(item['label']!), + value: item['value']!, + groupValue: tempSelectedTab, + onChanged: (value) => setState(() => tempSelectedTab = value!), + ); + }).toList(), + ]; - if (showProjectList) { - filterWidgets = accessibleProjects.isEmpty - ? [ - const Padding( - padding: EdgeInsets.all(12.0), - child: Center(child: Text('No Projects Assigned')), + if (tempSelectedTab == 'attendanceLogs') { + widgets.addAll([ + const Divider(), + Padding( + padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), + child: Align( + alignment: Alignment.centerLeft, + child: MyText.titleSmall( + "Date Range", + fontWeight: 600, + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + child: InkWell( + borderRadius: BorderRadius.circular(10), + onTap: () => widget.controller.selectDateRangeForAttendance( + context, + widget.controller, + ), + child: Ink( + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: Colors.grey.shade400), + borderRadius: BorderRadius.circular(10), + ), + padding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + child: Row( + children: [ + Icon(Icons.date_range, color: Colors.black87), + const SizedBox(width: 12), + Expanded( + child: Text( + getLabelText(), + style: const TextStyle( + fontSize: 16, + color: Colors.black87, + fontWeight: FontWeight.w500, + ), + overflow: TextOverflow.ellipsis, + ), + ), + const Icon(Icons.arrow_drop_down, color: Colors.black87), + ], + ), + ), + ), + ), + ]); + } + + return widgets; + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Drag handle + Padding( + padding: const EdgeInsets.only(top: 12, bottom: 8), + child: Center( + child: Container( + width: 40, + height: 4, + decoration: BoxDecoration( + color: Colors.grey[400], + borderRadius: BorderRadius.circular(4), + ), ), - ] - : accessibleProjects.map((project) { - final isSelected = - tempSelectedProjectId == project.id.toString(); - return ListTile( - dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - title: Text(project.name), - trailing: isSelected ? const Icon(Icons.check) : null, - onTap: () { - setState(() { - tempSelectedProjectId = project.id.toString(); - showProjectList = false; - }); - }, - ); - }).toList(); - } else { - final selectedProject = accessibleProjects.isNotEmpty - ? accessibleProjects.firstWhere( - (p) => p.id.toString() == tempSelectedProjectId, - orElse: () => accessibleProjects[0], - ) - : null; - - final selectedProjectName = selectedProject?.name ?? "Select Project"; - - filterWidgets = [ - Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), - child: Align( - alignment: Alignment.centerLeft, - child: MyText.titleSmall( - "Project", - fontWeight: 600, ), ), - ), - ListTile( - dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 16), - title: Text(selectedProjectName), - trailing: const Icon(Icons.arrow_drop_down), - onTap: () => setState(() => showProjectList = true), - ), - const Divider(), - Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), - child: Align( - alignment: Alignment.centerLeft, - child: MyText.titleSmall( - "View", - fontWeight: 600, - ), - ), - ), - ...filteredViewOptions.map((item) { - return RadioListTile( - dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 12), - title: Text(item['label']!), - value: item['value']!, - groupValue: tempSelectedTab, - onChanged: (value) => setState(() => tempSelectedTab = value!), - ); - }).toList(), - ]; - - if (tempSelectedTab == 'attendanceLogs') { - filterWidgets.addAll([ + if (showProjectList) ...buildProjectList() else ...buildMainFilters(), const Divider(), Padding( - padding: const EdgeInsets.fromLTRB(16, 12, 16, 4), - child: Align( - alignment: Alignment.centerLeft, - child: MyText.titleSmall( - "Date Range", - fontWeight: 600, + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + child: SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: const Color.fromARGB(255, 95, 132, 255), + padding: const EdgeInsets.symmetric(vertical: 12), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + ), + child: const Text('Apply Filter'), + onPressed: () { + Navigator.pop(context, { + 'projectId': tempSelectedProjectId, + 'selectedTab': tempSelectedTab, + }); + }, ), ), ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - child: InkWell( - borderRadius: BorderRadius.circular(10), - onTap: () => controller.selectDateRangeForAttendance( - context, - controller, - ), - child: Ink( - decoration: BoxDecoration( - color: const Color.fromARGB(255, 255, 255, 255), - border: Border.all(color: Colors.grey.shade400), - borderRadius: BorderRadius.circular(10), - ), - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 14), - child: Row( - children: [ - Icon(Icons.date_range, - color: const Color.fromARGB(255, 9, 9, 9)), - const SizedBox(width: 12), - Expanded( - child: Text( - getLabelText(), - style: const TextStyle( - fontSize: 16, - color: Colors.black87, - fontWeight: FontWeight.w500, - ), - overflow: TextOverflow.ellipsis, - ), - ), - const Icon(Icons.arrow_drop_down, - color: Color.fromARGB(255, 0, 0, 0)), - ], - ), - ), - ), - ), - ]); - } - } - - return Padding( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).viewInsets.bottom, + ], ), - child: SingleChildScrollView( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.only(top: 12, bottom: 8), - child: Center( - child: Container( - width: 40, - height: 4, - decoration: BoxDecoration( - color: Colors.grey[400], - borderRadius: BorderRadius.circular(4), - ), - ), - ), - ), - ...filterWidgets, - const Divider(), - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - child: SizedBox( - width: double.infinity, - child: ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: const Color.fromARGB(255, 95, 132, 255), - padding: const EdgeInsets.symmetric(vertical: 12), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - ), - child: const Text('Apply Filter'), - onPressed: () { - Navigator.pop(context, { - 'projectId': tempSelectedProjectId, - 'selectedTab': tempSelectedTab, - }); - }, - ), - ), - ), - ], - ), - ), - ); - }); + ), + ); } } -- 2.43.0 From aca2722ee4adfb762a024972702e61a1d6d46815 Mon Sep 17 00:00:00 2001 From: Vaibhav Surve Date: Sat, 31 May 2025 16:46:10 +0530 Subject: [PATCH 4/4] refactor: Update Checkbox fill color handling and enhance agreement toggle interaction --- lib/view/auth/login_screen.dart | 12 +++++- lib/view/auth/request_demo_bottom_sheet.dart | 39 ++++++++++++-------- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/lib/view/auth/login_screen.dart b/lib/view/auth/login_screen.dart index c3c6bb5..0c7f0e7 100644 --- a/lib/view/auth/login_screen.dart +++ b/lib/view/auth/login_screen.dart @@ -123,7 +123,17 @@ class _LoginScreenState extends State with UIMixin { shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(4), ), - fillColor: WidgetStatePropertyAll(contentTheme.secondary), + fillColor: MaterialStateProperty + .resolveWith( + (Set states) { + if (states + .contains(WidgetState.selected)) { + return Colors.blueAccent; + } + return Colors + .white; + }, + ), checkColor: contentTheme.onPrimary, visualDensity: getCompactDensity, materialTapTargetSize: diff --git a/lib/view/auth/request_demo_bottom_sheet.dart b/lib/view/auth/request_demo_bottom_sheet.dart index ffed24c..24c1dd6 100644 --- a/lib/view/auth/request_demo_bottom_sheet.dart +++ b/lib/view/auth/request_demo_bottom_sheet.dart @@ -147,22 +147,29 @@ class _OrganizationFormState extends State<_OrganizationForm> { 'Please select industry', ), const SizedBox(height: 10), - Row( - children: [ - Checkbox( - value: _agreed, - onChanged: (val) => setState(() => _agreed = val ?? false), - fillColor: MaterialStateProperty.all(Colors.white), - checkColor: Colors.white, - side: MaterialStateBorderSide.resolveWith( - (states) => - BorderSide(color: Colors.blueAccent, width: 2), - ), - ), - const Expanded( - child: Text('I agree to privacy policy & terms')), - ], - ), + GestureDetector( + onTap: () => setState(() => _agreed = !_agreed), + child: Row( + children: [ + Checkbox( + value: _agreed, + onChanged: (val) => setState(() => _agreed = val ?? false), + fillColor: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected)) { + return Colors.blueAccent; + } + return Colors.white; + }), + checkColor: Colors.white, + side: const BorderSide(color: Colors.blueAccent, width: 2), + ), + const Expanded( + child: Text('I agree to privacy policy & terms'), + ), + ], + ), +), + const SizedBox(height: 10), ElevatedButton( style: ElevatedButton.styleFrom( -- 2.43.0