mirror of
https://github.com/SyncrowIOT/web.git
synced 2025-07-09 22:57:21 +00:00
login Enhancements
This commit is contained in:
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -23,7 +24,8 @@ class LoginWebPage extends StatefulWidget {
|
|||||||
State<LoginWebPage> createState() => _LoginWebPageState();
|
State<LoginWebPage> createState() => _LoginWebPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout {
|
class _LoginWebPageState extends State<LoginWebPage>
|
||||||
|
with HelperResponsiveLayout {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -58,7 +60,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
_scrollController = ScrollController();
|
_scrollController = ScrollController();
|
||||||
|
|
||||||
void _scrollToCenter() {
|
void _scrollToCenter() {
|
||||||
final double middlePosition = _scrollController.position.maxScrollExtent / 2;
|
final double middlePosition =
|
||||||
|
_scrollController.position.maxScrollExtent / 2;
|
||||||
_scrollController.animateTo(
|
_scrollController.animateTo(
|
||||||
middlePosition,
|
middlePosition,
|
||||||
duration: const Duration(seconds: 1),
|
duration: const Duration(seconds: 1),
|
||||||
@ -120,7 +123,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
Expanded(
|
Expanded(
|
||||||
flex: 2,
|
flex: 2,
|
||||||
child: _buildLoginFormFields(context, loginBloc, size),
|
child: _buildLoginFormFields(
|
||||||
|
context, loginBloc, size),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const Spacer(),
|
||||||
],
|
],
|
||||||
@ -131,12 +135,14 @@ class _LoginWebPageState extends State<LoginWebPage> 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(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withOpacity(0.1),
|
color: Colors.white.withOpacity(0.1),
|
||||||
@ -146,8 +152,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
child: Form(
|
child: Form(
|
||||||
key: loginBloc.loginFormKey,
|
key: loginBloc.loginFormKey,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding: EdgeInsets.symmetric(
|
||||||
EdgeInsets.symmetric(horizontal: size.width * 0.02, vertical: size.width * 0.003),
|
horizontal: size.width * 0.02, vertical: size.width * 0.003),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@ -175,82 +181,190 @@ class _LoginWebPageState extends State<LoginWebPage> 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<String>(
|
||||||
|
// 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<String>(
|
||||||
|
// 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<Widget>((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(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
"Country/Region",
|
"Country/Region",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
fontSize: 14,
|
||||||
.bodySmall!
|
fontWeight: FontWeight.w400,
|
||||||
.copyWith(fontSize: 14, fontWeight: FontWeight.w400),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
SizedBox(
|
Container(
|
||||||
width: size.width * 0.8,
|
height: size.height * 0.05,
|
||||||
child: LayoutBuilder(
|
decoration:const BoxDecoration(
|
||||||
builder: (context, constraints) {
|
color: ColorsManager.boxColor,
|
||||||
return DropdownButtonFormField<String>(
|
borderRadius: BorderRadius.all(Radius.circular(8))),
|
||||||
value: loginBloc.regionList!.any((region) => region.id == loginBloc.regionUuid)
|
width: size.width * 0.9,
|
||||||
? loginBloc.regionUuid
|
child: DropdownButtonHideUnderline(
|
||||||
: null,
|
child: DropdownButton2<String>(
|
||||||
validator: loginBloc.validateRegion,
|
isExpanded: true,
|
||||||
icon: const Icon(
|
hint: Text(
|
||||||
Icons.keyboard_arrow_down_outlined,
|
'Select your region/country',
|
||||||
size: 20,
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
),
|
color: ColorsManager.grayColor,
|
||||||
decoration: textBoxDecoration()!.copyWith(
|
fontWeight: FontWeight.w400,
|
||||||
errorStyle: const TextStyle(height: 0),
|
),
|
||||||
contentPadding: const EdgeInsets.symmetric(
|
overflow: TextOverflow.ellipsis,
|
||||||
vertical: 12,
|
),
|
||||||
horizontal: 10,
|
items: loginBloc.regionList!.map((RegionModel region) {
|
||||||
|
return DropdownMenuItem<String>(
|
||||||
|
value: region.id,
|
||||||
|
child: Text(
|
||||||
|
region.name,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 1,
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
hint: Text(
|
}).toList(),
|
||||||
'Select your region/country',
|
value: loginBloc.regionList!.any(
|
||||||
style: Theme.of(context)
|
(region) => region.id == loginBloc.regionUuid,
|
||||||
.textTheme
|
)
|
||||||
.bodySmall!
|
? loginBloc.regionUuid
|
||||||
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
|
: null,
|
||||||
overflow: TextOverflow.ellipsis,
|
onChanged: (String? value) {
|
||||||
),
|
if (value != null) {
|
||||||
isDense: true,
|
loginBloc.add(CheckEnableEvent());
|
||||||
style: const TextStyle(color: Colors.black),
|
loginBloc.add(SelectRegionEvent(val: value));
|
||||||
items: loginBloc.regionList!.map((RegionModel region) {
|
}
|
||||||
return DropdownMenuItem<String>(
|
},
|
||||||
value: region.id,
|
buttonStyleData: const ButtonStyleData(
|
||||||
child: Container(
|
padding: EdgeInsets.symmetric(horizontal: 16),
|
||||||
constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40),
|
height: 40,
|
||||||
child: Text(
|
width: double.infinity,
|
||||||
region.name,
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
dropdownStyleData: DropdownStyleData(
|
||||||
maxLines: 1,
|
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());
|
searchMatchFn: (item, searchValue) {
|
||||||
loginBloc.add(SelectRegionEvent(val: value!));
|
// 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) {
|
onMenuStateChange: (isOpen) {
|
||||||
return loginBloc.regionList!.map<Widget>((region) {
|
if (!isOpen) {
|
||||||
return Container(
|
textEditingController.clear();
|
||||||
constraints: BoxConstraints(maxWidth: constraints.maxWidth - 40),
|
}
|
||||||
child: Text(
|
},
|
||||||
region.name,
|
),
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
maxLines: 1,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}).toList();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -280,10 +394,9 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
decoration: textBoxDecoration()!.copyWith(
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
errorStyle: const TextStyle(height: 0),
|
errorStyle: const TextStyle(height: 0),
|
||||||
hintText: 'Enter your email address',
|
hintText: 'Enter your email address',
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
color: ColorsManager.grayColor,
|
||||||
.bodySmall!
|
fontWeight: FontWeight.w400)),
|
||||||
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400)),
|
|
||||||
style: const TextStyle(color: Colors.black),
|
style: const TextStyle(color: Colors.black),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -315,17 +428,18 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
controller: loginBloc.loginPasswordController,
|
controller: loginBloc.loginPasswordController,
|
||||||
decoration: textBoxDecoration()!.copyWith(
|
decoration: textBoxDecoration()!.copyWith(
|
||||||
hintText: 'At least 8 characters',
|
hintText: 'At least 8 characters',
|
||||||
hintStyle: Theme.of(context)
|
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
|
||||||
.bodySmall!
|
|
||||||
.copyWith(color: ColorsManager.grayColor, fontWeight: FontWeight.w400),
|
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
loginBloc.add(PasswordVisibleEvent(newValue: loginBloc.obscureText));
|
loginBloc.add(
|
||||||
|
PasswordVisibleEvent(newValue: loginBloc.obscureText));
|
||||||
},
|
},
|
||||||
icon: SizedBox(
|
icon: SizedBox(
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
loginBloc.obscureText ? Assets.visiblePassword : Assets.invisiblePassword,
|
loginBloc.obscureText
|
||||||
|
? Assets.visiblePassword
|
||||||
|
: Assets.invisiblePassword,
|
||||||
height: 15,
|
height: 15,
|
||||||
width: 15,
|
width: 15,
|
||||||
),
|
),
|
||||||
@ -353,10 +467,10 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"Forgot Password?",
|
"Forgot Password?",
|
||||||
style: Theme.of(context)
|
style: Theme.of(context).textTheme.bodySmall!.copyWith(
|
||||||
.textTheme
|
color: Colors.black,
|
||||||
.bodySmall!
|
fontSize: 14,
|
||||||
.copyWith(color: Colors.black, fontSize: 14, fontWeight: FontWeight.w400),
|
fontWeight: FontWeight.w400),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -419,7 +533,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildSignInButton(BuildContext context, AuthBloc loginBloc, Size size) {
|
Widget _buildSignInButton(
|
||||||
|
BuildContext context, AuthBloc loginBloc, Size size) {
|
||||||
return Row(
|
return Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -460,7 +575,8 @@ class _LoginWebPageState extends State<LoginWebPage> with HelperResponsiveLayout
|
|||||||
SizedBox(
|
SizedBox(
|
||||||
child: Text(
|
child: Text(
|
||||||
loginBloc.validate,
|
loginBloc.validate,
|
||||||
style: const TextStyle(fontWeight: FontWeight.w700, color: ColorsManager.red),
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.w700, color: ColorsManager.red),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
16
pubspec.lock
16
pubspec.lock
@ -89,6 +89,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
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:
|
equatable:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -43,9 +43,11 @@ dependencies:
|
|||||||
get_it: ^7.6.7
|
get_it: ^7.6.7
|
||||||
flutter_secure_storage: ^9.2.2
|
flutter_secure_storage: ^9.2.2
|
||||||
shared_preferences: ^2.3.0
|
shared_preferences: ^2.3.0
|
||||||
|
dropdown_button2: ^2.3.9
|
||||||
data_table_2: ^2.5.15
|
data_table_2: ^2.5.15
|
||||||
go_router:
|
go_router:
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
|
dropdown_search: ^5.0.6
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Reference in New Issue
Block a user