From 418f5d5406d727e0017edc6ae97c8510877c9a37 Mon Sep 17 00:00:00 2001 From: mohammad Date: Mon, 2 Sep 2024 16:59:25 +0300 Subject: [PATCH] login Enhancements --- lib/pages/auth/view/login_web_page.dart | 290 +++++++++++++++++------- pubspec.lock | 16 ++ pubspec.yaml | 2 + 3 files changed, 221 insertions(+), 87 deletions(-) diff --git a/lib/pages/auth/view/login_web_page.dart b/lib/pages/auth/view/login_web_page.dart index 1c638287..13492c45 100644 --- a/lib/pages/auth/view/login_web_page.dart +++ b/lib/pages/auth/view/login_web_page.dart @@ -1,3 +1,4 @@ +import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -23,7 +24,8 @@ class LoginWebPage extends StatefulWidget { State createState() => _LoginWebPageState(); } -class _LoginWebPageState extends State with HelperResponsiveLayout { +class _LoginWebPageState extends State + with HelperResponsiveLayout { @override Widget build(BuildContext context) { return Scaffold( @@ -58,7 +60,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout _scrollController = ScrollController(); void _scrollToCenter() { - final double middlePosition = _scrollController.position.maxScrollExtent / 2; + final double middlePosition = + _scrollController.position.maxScrollExtent / 2; _scrollController.animateTo( middlePosition, duration: const Duration(seconds: 1), @@ -120,7 +123,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout const Spacer(), Expanded( flex: 2, - child: _buildLoginFormFields(context, loginBloc, size), + child: _buildLoginFormFields( + context, loginBloc, size), ), const Spacer(), ], @@ -131,12 +135,14 @@ class _LoginWebPageState extends State with HelperResponsiveLayout ), ), ), - if (state is AuthLoading) const Center(child: CircularProgressIndicator()) + if (state is AuthLoading) + const Center(child: CircularProgressIndicator()) ], ); } - Widget _buildLoginFormFields(BuildContext context, AuthBloc loginBloc, Size size) { + Widget _buildLoginFormFields( + BuildContext context, AuthBloc loginBloc, Size size) { return Container( decoration: BoxDecoration( color: Colors.white.withOpacity(0.1), @@ -146,8 +152,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout child: Form( key: loginBloc.loginFormKey, child: Padding( - padding: - EdgeInsets.symmetric(horizontal: size.width * 0.02, vertical: size.width * 0.003), + padding: EdgeInsets.symmetric( + horizontal: size.width * 0.02, vertical: size.width * 0.003), child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.start, @@ -175,82 +181,190 @@ class _LoginWebPageState extends State with HelperResponsiveLayout ); } - Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) { + // Widget _buildDropdownField(BuildContext context, AuthBloc loginBloc, Size size) { + // return Column( + // crossAxisAlignment: CrossAxisAlignment.start, + // mainAxisAlignment: MainAxisAlignment.start, + // children: [ + // Text( + // "Country/Region", + // style: Theme.of(context) + // .textTheme + // .bodySmall! + // .copyWith(fontSize: 14, fontWeight: FontWeight.w400), + // ), + // const SizedBox(height: 10), + // SizedBox( + // width: size.width * 0.8, + // child: LayoutBuilder( + // builder: (context, constraints) { + // return DropdownButtonFormField( + // value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid) + // ? loginBloc.regionUuid + // : null, + // validator: loginBloc.validateRegion, + // icon: const Icon( + // Icons.keyboard_arrow_down_outlined, + // size: 20, + // ), + // decoration: textBoxDecoration()!.copyWith( + // errorStyle: const TextStyle(height: 0), + // contentPadding: const EdgeInsets.symmetric( + // vertical: 12, + // horizontal: 10, + // ), + // ), + // hint: Text( + // 'Select your region/country', + // style: Theme.of(context) + // .textTheme + // .bodySmall! + // .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400), + // overflow: TextOverflow.ellipsis, + // ), + // isDense: true, + // style: const TextStyle(color: Colors.black), + // items: loginBloc.regionList!.map((RegionModel region) { + // return DropdownMenuItem( + // value: region.id, + // child: Container( + // constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), + // child: Text( + // region.name, + // overflow: TextOverflow.ellipsis, + // maxLines: 1, + // ), + // ), + // ); + // }).toList(), + // onChanged: (String? value) { + // loginBloc.add(CheckEnableEvent()); + // loginBloc.add(SelectRegionEvent(val: value!)); + // }, + // dropdownColor: Colors.white, + // menuMaxHeight: size.height * 0.45, + // selectedItemBuilder: (context) { + // return loginBloc.regionList!.map((region) { + // return Container( + // constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), + // child: Text( + // region.name, + // overflow: TextOverflow.ellipsis, + // maxLines: 1, + // ), + // ); + // }).toList(); + // }, + // ); + // }, + // ), + // ), + // ], + // ); + // } + + Widget _buildDropdownField( + BuildContext context, AuthBloc loginBloc, Size size) { + final TextEditingController textEditingController = TextEditingController(); return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "Country/Region", - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(fontSize: 14, fontWeight: FontWeight.w400), + style: Theme.of(context).textTheme.bodySmall!.copyWith( + fontSize: 14, + fontWeight: FontWeight.w400, + ), ), const SizedBox(height: 10), - SizedBox( - width: size.width * 0.8, - child: LayoutBuilder( - builder: (context, constraints) { - return DropdownButtonFormField( - value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid) - ? loginBloc.regionUuid - : null, - validator: loginBloc.validateRegion, - icon: const Icon( - Icons.keyboard_arrow_down_outlined, - size: 20, - ), - decoration: textBoxDecoration()!.copyWith( - errorStyle: const TextStyle(height: 0), - contentPadding: const EdgeInsets.symmetric( - vertical: 12, - horizontal: 10, + Container( + height: size.height * 0.05, + decoration:const BoxDecoration( + color: ColorsManager.boxColor, + borderRadius: BorderRadius.all(Radius.circular(8))), + width: size.width * 0.9, + child: DropdownButtonHideUnderline( + child: DropdownButton2( + isExpanded: true, + hint: Text( + 'Select your region/country', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400, + ), + overflow: TextOverflow.ellipsis, + ), + items: loginBloc.regionList!.map((RegionModel region) { + return DropdownMenuItem( + value: region.id, + child: Text( + region.name, + overflow: TextOverflow.ellipsis, + maxLines: 1, ), - ), - hint: Text( - 'Select your region/country', - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400), - overflow: TextOverflow.ellipsis, - ), - isDense: true, - style: const TextStyle(color: Colors.black), - items: loginBloc.regionList!.map((RegionModel region) { - return DropdownMenuItem( - value: region.id, - child: Container( - constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), - child: Text( - region.name, - overflow: TextOverflow.ellipsis, - maxLines: 1, + ); + }).toList(), + value: loginBloc.regionList!.any( + (region) => region.id == loginBloc.regionUuid, + ) + ? loginBloc.regionUuid + : null, + onChanged: (String? value) { + if (value != null) { + loginBloc.add(CheckEnableEvent()); + loginBloc.add(SelectRegionEvent(val: value)); + } + }, + buttonStyleData: const ButtonStyleData( + padding: EdgeInsets.symmetric(horizontal: 16), + height: 40, + width: double.infinity, + ), + dropdownStyleData: DropdownStyleData( + maxHeight: size.height * 0.45, + ), + menuItemStyleData: const MenuItemStyleData( + height: 40, + ), + dropdownSearchData: DropdownSearchData( + searchController: textEditingController, + searchInnerWidgetHeight: 50, + searchInnerWidget: Container( + height: 50, + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + child: TextFormField( + style: TextStyle(color: Colors.black), + controller: textEditingController, + decoration: textBoxDecoration()!.copyWith( + errorStyle: const TextStyle(height: 0), + contentPadding: const EdgeInsets.symmetric( + vertical: 12, + horizontal: 10, ), ), - ); - }).toList(), - onChanged: (String? value) { - loginBloc.add(CheckEnableEvent()); - loginBloc.add(SelectRegionEvent(val: value!)); + ), + ), + + searchMatchFn: (item, searchValue) { + // Ensure both item value and search value are compared in lowercase for a case-insensitive match. + final itemValue = item.value?.toLowerCase() ?? ''; + final search = searchValue.toLowerCase().trim(); + + // Debugging print statement to ensure values are captured correctly. + print('searchValue == $search'); + + // Return true if the item value contains the search term. + return itemValue.contains(search); }, - dropdownColor: Colors.white, - menuMaxHeight: size.height * 0.45, - selectedItemBuilder: (context) { - return loginBloc.regionList!.map((region) { - return Container( - constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40), - child: Text( - region.name, - overflow: TextOverflow.ellipsis, - maxLines: 1, - ), - ); - }).toList(); - }, - ); - }, + + ), + onMenuStateChange: (isOpen) { + if (!isOpen) { + textEditingController.clear(); + } + }, + ), ), ), ], @@ -280,10 +394,9 @@ class _LoginWebPageState extends State with HelperResponsiveLayout decoration: textBoxDecoration()!.copyWith( errorStyle: const TextStyle(height: 0), hintText: 'Enter your email address', - hintStyle: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400)), + hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, + fontWeight: FontWeight.w400)), style: const TextStyle(color: Colors.black), ), ), @@ -315,17 +428,18 @@ class _LoginWebPageState extends State with HelperResponsiveLayout controller: loginBloc.loginPasswordController, decoration: textBoxDecoration()!.copyWith( hintText: 'At least 8 characters', - hintStyle: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400), + hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( + color: ColorsManager.grayColor, fontWeight: FontWeight.w400), suffixIcon: IconButton( onPressed: () { - loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText)); + loginBloc.add( + PasswordVisibleEvent(newValue: loginBloc.obscureText)); }, icon: SizedBox( child: SvgPicture.asset( - loginBloc.obscureText ? Assets.visiblePassword : Assets.invisiblePassword, + loginBloc.obscureText + ? Assets.visiblePassword + : Assets.invisiblePassword, height: 15, width: 15, ), @@ -353,10 +467,10 @@ class _LoginWebPageState extends State with HelperResponsiveLayout }, child: Text( "Forgot Password?", - style: Theme.of(context) - .textTheme - .bodySmall! - .copyWith(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400), + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Colors.black, + fontSize: 14, + fontWeight: FontWeight.w400), ), ), ], @@ -419,7 +533,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout ); } - Widget _buildSignInButton(BuildContext context, AuthBloc loginBloc, Size size) { + Widget _buildSignInButton( + BuildContext context, AuthBloc loginBloc, Size size) { return Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, @@ -460,7 +575,8 @@ class _LoginWebPageState extends State with HelperResponsiveLayout SizedBox( child: Text( loginBloc.validate, - style: const TextStyle(fontWeight: FontWeight.w700, color: ColorsManager.red), + style: const TextStyle( + fontWeight: FontWeight.w700, color: ColorsManager.red), ), ) ], diff --git a/pubspec.lock b/pubspec.lock index 9a9cd6a8..3da608f2 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -89,6 +89,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.1" + dropdown_button2: + dependency: "direct main" + description: + name: dropdown_button2 + sha256: b0fe8d49a030315e9eef6c7ac84ca964250155a6224d491c1365061bc974a9e1 + url: "https://pub.dev" + source: hosted + version: "2.3.9" + dropdown_search: + dependency: "direct main" + description: + name: dropdown_search + sha256: "55106e8290acaa97ed15bea1fdad82c3cf0c248dd410e651f5a8ac6870f783ab" + url: "https://pub.dev" + source: hosted + version: "5.0.6" equatable: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a8e96a9b..ea0138ea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -43,9 +43,11 @@ dependencies: get_it: ^7.6.7 flutter_secure_storage: ^9.2.2 shared_preferences: ^2.3.0 + dropdown_button2: ^2.3.9 data_table_2: ^2.5.15 go_router: intl: ^0.19.0 + dropdown_search: ^5.0.6 dev_dependencies: flutter_test: