feat:login page done

This commit is contained in:
jahongireshonqulov
2025-10-29 12:00:33 +05:00
parent d3ad5b8ddd
commit 2ed2c430c0
27 changed files with 524 additions and 178 deletions

View File

@@ -1,4 +1,5 @@
import 'package:food_delivery_client/feature/auth/domain/usecases/login_usecase.dart';
import 'package:food_delivery_client/feature/common/presentation/widgets/w_toastification.dart';
import 'package:food_delivery_client/food_delivery_client.dart';
part 'login_event.dart';
@@ -10,8 +11,10 @@ part 'login_bloc.freezed.dart';
@injectable
class LoginBloc extends Bloc<LoginEvent, LoginState> {
final LoginUseCase _loginUseCase;
final StorageService _storageService;
LoginBloc(this._loginUseCase) : super(const LoginState()) {
LoginBloc(this._loginUseCase, this._storageService)
: super(const LoginState()) {
on<_Login>(_onLogin);
}
@@ -23,10 +26,12 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
response.fold(
(l) {
log("${l.errorMessage}");
showErrorToast(l.errorMessage);
emit(state.copyWith(status: RequestStatus.error));
},
(r) {
log(r.token);
showSuccessToast("Login success");
_storageService.setString(key: AppLocaleKeys.token, value: r.token);
emit(state.copyWith(status: RequestStatus.loaded));
},
);

View File

@@ -0,0 +1,12 @@
import '../../../../../food_delivery_client.dart';
class ForgotPasswordPage extends StatelessWidget {
const ForgotPasswordPage({super.key});
@override
Widget build(BuildContext context) {
return WLayout(
child: Scaffold(body: Column(children: [Text('Forgot password')])),
);
}
}

View File

@@ -10,7 +10,7 @@ class LoginPage extends StatelessWidget {
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => sl<LoginBloc>(),
child: WLayout(
child: WLayout(
child: Scaffold(
body: WLoginBody(),
),

View File

@@ -1,7 +1,5 @@
import 'package:flutter/cupertino.dart';
import 'package:food_delivery_client/core/helpers/formatters.dart';
import 'package:food_delivery_client/feature/auth/domain/usecases/login_usecase.dart';
import '../../../../../../food_delivery_client.dart';
import '../../../blocs/login_bloc/login_bloc.dart';
@@ -38,86 +36,127 @@ class _WLoginBodyState extends State<WLoginBody> {
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
45.verticalSpace,
Align(
alignment: AlignmentGeometry.center,
child: Text("Let's go", style: AppTextStyles.size24Bold),
),
20.verticalSpace,
Text(
'Phone number',
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.c6A6E7F,
),
),
5.verticalSpace,
AppTextFormField(
height: 50,
hintText: "Enter phone number",
prefixIcon: Text(
"+ 998",
style: AppTextStyles.size16Regular.copyWith(fontSize: 16),
),
borderRadius: AppUtils.kBorderRadius8,
controller: _phoneController,
keyBoardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
Formatters.phoneFormatter,
LengthLimitingTextInputFormatter(12),
],
),
20.verticalSpace,
Text(
'Password',
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.c6A6E7F,
),
),
5.verticalSpace,
AppTextFormField(
height: 50,
hintText: "Enter password",
keyBoardType: TextInputType.text,
borderRadius: AppUtils.kBorderRadius8,
controller: _passwordController,
),
Align(
alignment: AlignmentGeometry.centerRight,
child: TextButton(
onPressed: () {},
child: Text('Forgot password'),
),
),
const Spacer(),
AppButton(
name: "Continue",
isLoading: state.status.isLoading(),
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
context.read<LoginBloc>().add(
LoginEvent.login(
LoginParams(
phoneNumber:
"+998${_phoneController.text.trim().replaceAll(" ", "")}",
password: _passwordController.text.trim(),
child: LayoutBuilder(
builder: (context, constraints) {
return ConstrainedBox(
constraints: BoxConstraints(),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
45.verticalSpace,
Align(
alignment: AlignmentGeometry.center,
child: Text(context.loc.login, style: AppTextStyles.size24Bold),
),
20.verticalSpace,
Text(
context.loc.phone_number,
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.c6A6E7F,
),
),
5.verticalSpace,
AppTextFormField(
height: 50,
hintText: context.loc.enter_phone_number,
prefixIcon: Text(
"+ 998",
style: AppTextStyles.size16Regular.copyWith(
fontSize: 16,
),
),
);
}
},
borderRadius: 15,
backgroundColor: AppColors.c34A853,
),
20.verticalSpace,
],
).paddingSymmetric(horizontal: 16),
borderRadius: AppUtils.kBorderRadius8,
controller: _phoneController,
keyBoardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
Formatters.phoneFormatter,
LengthLimitingTextInputFormatter(12),
],
),
20.verticalSpace,
Text(
context.loc.password,
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.c6A6E7F,
),
),
5.verticalSpace,
AppTextFormField(
height: 50,
obscureText: true,
hintText: context.loc.enter_password,
keyBoardType: TextInputType.text,
borderRadius: AppUtils.kBorderRadius8,
controller: _passwordController,
),
Align(
alignment: AlignmentGeometry.centerRight,
child: TextButton(
onPressed: () {
context.push(Routes.forgotPassword);
},
child: Text(
context.loc.forgot_password,
style: AppTextStyles.size14Medium.copyWith(
color: AppColors.c34A853,
),
),
),
),
const Spacer(),
AppButton(
name: context.loc.Continue,
isLoading: state.status.isLoading(),
trailing: SvgPicture.asset(
AppIcons.icArrowRightLight,
).paddingOnly(left: 10),
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
context.read<LoginBloc>().add(
LoginEvent.login(
LoginParams(
phoneNumber:
"+998${_phoneController.text.trim().replaceAll(" ", "")}",
password: _passwordController.text.trim(),
),
),
);
}
},
borderRadius: 15,
backgroundColor: AppColors.c34A853,
),
20.verticalSpace,
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
context.loc.dont_have_account,
style: AppTextStyles.size14Medium,
),
TextButton(
onPressed: () {
context.pushReplacement(Routes.register);
},
child: Text(
context.loc.sign_up,
style: AppTextStyles.size15Bold.copyWith(
color: AppColors.c34A853,
),
),
),
],
),
20.verticalSpace,
],
).paddingSymmetric(horizontal: 16),
);
},
),
);
},
);

View File

@@ -0,0 +1,12 @@
import '../../../../../food_delivery_client.dart';
class RegisterPage extends StatelessWidget {
const RegisterPage({super.key});
@override
Widget build(BuildContext context) {
return WLayout(
child: Scaffold(body: Column(children: [Text("register")])),
);
}
}

View File

@@ -11,7 +11,7 @@ class AppButton extends StatelessWidget {
this.height,
this.textColor,
this.width,
this.action,
this.leading,
this.trailing,
this.mainAxisAlignment,
this.isLoading = false,
@@ -25,7 +25,7 @@ class AppButton extends StatelessWidget {
final double? borderRadius;
final double? width;
final double? height;
final Widget? action;
final Widget? leading;
final Widget? trailing;
final bool isLoading;
final MainAxisAlignment? mainAxisAlignment;
@@ -57,7 +57,7 @@ class AppButton extends StatelessWidget {
mainAxisAlignment:
mainAxisAlignment ?? MainAxisAlignment.center,
children: [
action ?? AppUtils.kSizedBox,
leading?? AppUtils.kSizedBox,
Text(
name,
style: AppTextStyles.size16Bold.copyWith(

View File

@@ -1,7 +1,7 @@
import '../../../../food_delivery_client.dart';
class AppTextFormField extends StatelessWidget {
AppTextFormField({
class AppTextFormField extends StatefulWidget {
const AppTextFormField({
super.key,
this.maxLines,
this.minLines,
@@ -21,6 +21,7 @@ class AppTextFormField extends StatelessWidget {
this.focusNode,
this.borderRadius,
this.height,
this.suffixIcon,
});
final int? maxLines;
@@ -32,63 +33,86 @@ class AppTextFormField extends StatelessWidget {
final Function(String? value)? onChanged;
final List<TextInputFormatter>? inputFormatters;
final TextInputType? keyBoardType;
FormFieldValidator<String>? validator;
final bool obscureText;
final Color? fillColor;
final String? hintText;
final TextStyle? hintTextStyle;
late final Widget? prefixIcon;
final Widget? prefixIcon;
final String? prefixSvgPath;
final FocusNode? focusNode;
final BorderRadius? borderRadius;
final double? height;
final Widget? suffixIcon;
@override
State<AppTextFormField> createState() => _AppTextFormFieldState();
}
class _AppTextFormFieldState extends State<AppTextFormField> {
FormFieldValidator<String>? validator;
bool visibility = true;
@override
Widget build(BuildContext context) {
return SizedBox(
height: height ?? 44,
height: widget.height ?? 44,
child: TextFormField(
enabled: true,
autofocus: true,
maxLines: maxLines ?? 1,
minLines: minLines ?? 1,
onChanged: onChanged,
focusNode: focusNode,
inputFormatters: inputFormatters,
keyboardType: keyBoardType,
style: textStyle ?? AppTextStyles.size16Regular,
onTap: onTap,
textAlign: textAlign ?? TextAlign.start,
controller: controller,
maxLines: widget.maxLines ?? 1,
minLines: widget.minLines ?? 1,
onChanged: widget.onChanged,
focusNode: widget.focusNode,
inputFormatters: widget.inputFormatters,
keyboardType: widget.keyBoardType,
style: widget.textStyle ?? AppTextStyles.size16Regular,
onTap: widget.onTap,
textAlign: widget.textAlign ?? TextAlign.start,
controller: widget.controller,
validator: validator,
obscureText: widget.obscureText?visibility:false,
obscuringCharacter: "*",
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: InputDecoration(
enabled: true,
filled: true,
fillColor: fillColor ?? AppColors.cEEEEEE,
hintText: hintText,
hintStyle: hintTextStyle,
fillColor: widget.fillColor ?? AppColors.cEEEEEE,
hintText: widget.hintText,
hintStyle: widget.hintTextStyle,
suffixIcon: widget.obscureText
? IconButton(
onPressed: () {
setState(() {
visibility = !visibility;
});
},
icon: Icon(
visibility ? Icons.visibility : Icons.visibility_off,
),
)
: widget.suffixIcon,
prefixIconConstraints: BoxConstraints(minHeight: 24, minWidth: 0),
prefixIcon: prefixIcon?.paddingOnly(left: 10).paddingAll(3),
prefixIcon: widget.prefixIcon?.paddingOnly(left: 10).paddingAll(3),
contentPadding: EdgeInsets.symmetric(vertical: 12, horizontal: 12),
border: OutlineInputBorder(
borderRadius: borderRadius ?? AppUtils.kBorderRadius40,
borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40,
borderSide: BorderSide.none,
),
errorBorder: OutlineInputBorder(
borderRadius: borderRadius ?? AppUtils.kBorderRadius40,
borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40,
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: borderRadius ?? AppUtils.kBorderRadius40,
borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40,
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: borderRadius ?? AppUtils.kBorderRadius40,
borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40,
borderSide: BorderSide.none,
),
disabledBorder: OutlineInputBorder(
borderRadius: borderRadius ?? AppUtils.kBorderRadius40,
borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40,
borderSide: BorderSide.none,
),
),

View File

@@ -0,0 +1,43 @@
import 'package:toastification/toastification.dart';
import '../../../../food_delivery_client.dart';
showErrorToast(String? message) {
toastification.show(
context: navigatorKey.currentContext,
type: ToastificationType.error,
style: ToastificationStyle.fillColored,
title: Text(message ?? "", maxLines: 5),
autoCloseDuration: TimeDelayConst.duration3,
);
}
showSuccessToast(String message) {
toastification.show(
context: navigatorKey.currentContext,
type: ToastificationType.success,
style: ToastificationStyle.fillColored,
title: Text(message, maxLines: 5),
autoCloseDuration: TimeDelayConst.duration3,
);
}
showWarningToast(String message) {
toastification.show(
context: navigatorKey.currentContext,
type: ToastificationType.warning,
style: ToastificationStyle.fillColored,
title: Text(message, maxLines: 5),
autoCloseDuration: TimeDelayConst.duration3,
);
}
showInfoToast(String message) {
toastification.show(
alignment: Alignment.bottomCenter,
context: navigatorKey.currentContext,
type: ToastificationType.info,
style: ToastificationStyle.fillColored,
title: Text(message, maxLines: 5),
autoCloseDuration: TimeDelayConst.duration3,
);
}