Skip to content

Commit

Permalink
Merge pull request #9 from Hegazy02/features/loginView
Browse files Browse the repository at this point in the history
create login cubit
  • Loading branch information
Hegazy02 authored Jun 9, 2024
2 parents c1ff1c7 + e4b03ec commit 765e737
Show file tree
Hide file tree
Showing 16 changed files with 416 additions and 158 deletions.
36 changes: 36 additions & 0 deletions lib/core/helpers/app_regex.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class AppRegex {
static bool isEmailValid(String email) {
return RegExp(r'^.+@[a-zA-Z]+\.{1}[a-zA-Z]+(\.{0,1}[a-zA-Z]+)$')
.hasMatch(email);
}

static bool isPasswordValid(String password) {
return RegExp(
r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$")
.hasMatch(password);
}

static bool isPhoneNumberValid(String phoneNumber) {
return RegExp(r'^(010|011|012|015)[0-9]{8}$').hasMatch(phoneNumber);
}

static bool hasLowerCase(String password) {
return RegExp(r'^(?=.*[a-z])').hasMatch(password);
}

static bool hasUpperCase(String password) {
return RegExp(r'^(?=.*[A-Z])').hasMatch(password);
}

static bool hasNumber(String password) {
return RegExp(r'^(?=.*?[0-9])').hasMatch(password);
}

static bool hasSpecialCharacter(String password) {
return RegExp(r'^(?=.*?[#?!@$%^&*-])').hasMatch(password);
}

static bool hasMinLength(String password) {
return RegExp(r'^(?=.{8,})').hasMatch(password);
}
}
19 changes: 19 additions & 0 deletions lib/core/helpers/extentions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,22 @@ extension Navigation on BuildContext {

void pop() => Navigator.of(this).pop();
}

extension LoadingExtension on BuildContext {
void showLoading() {
showDialog(
context: this,
barrierDismissible: false,
builder: (_) => const Center(child: CircularProgressIndicator()));
}

void hideLoading() => Navigator.of(this).pop();

void showError(String? message) {
showDialog(
context: this,
builder: (_) => AlertDialog(
content: Text(message ?? "Something went wrong"),
));
}
}
3 changes: 3 additions & 0 deletions lib/core/routing/app_router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:doctorito/core/di/dependency_injection.dart';
import 'package:doctorito/core/routing/routes.dart';
import 'package:doctorito/features/auth/presentation/view_model/login/login_cubit.dart';
import 'package:doctorito/features/auth/presentation/views/login_view.dart';
import 'package:doctorito/features/home/presentation/views/home_view.dart';
import 'package:doctorito/features/onboarding/presentation/views/on_boarding_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
Expand All @@ -19,6 +20,8 @@ class AppRouter {
));
case Routes.signUpView:
return MaterialPageRoute(builder: (_) => const OnBoardingView());
case Routes.homeView:
return MaterialPageRoute(builder: (_) => const HomeView());

default:
return MaterialPageRoute(
Expand Down
1 change: 1 addition & 0 deletions lib/core/routing/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ class Routes {
static const String onBoardingView = "/onBoardingView";
static const String loginView = "/loginView";
static const String signUpView = "/signUpView";
static const String homeView = "/homeView";
}
22 changes: 14 additions & 8 deletions lib/features/auth/presentation/view_model/login/login_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,26 @@ import 'package:bloc/bloc.dart';
import 'package:doctorito/features/auth/data/models/login_request_body.dart';
import 'package:doctorito/features/auth/data/repos/login_repo.dart';
import 'package:doctorito/features/auth/presentation/view_model/login/login_state.dart';
import 'package:flutter/material.dart';

class LoginCubit extends Cubit<LoginState> {
final LoginRepo _loginRepo;
LoginCubit(this._loginRepo) : super(const LoginState.initial());

final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
final formKey = GlobalKey<FormState>();
Future<void> login(LoginRequestBody loginRequestBody) async {
emit(const LoginState.loading());
if (formKey.currentState!.validate()) {
emit(const LoginState.loading());

final result = await _loginRepo.login(loginRequestBody);
final result = await _loginRepo.login(loginRequestBody);

result.when(success: (loginResponse) {
emit(LoginState.success(loginResponse: loginResponse));
}, failure: (error) {
emit(LoginState.error(error: error));
});
result.when(success: (loginResponse) {
emit(LoginState.success(loginResponse: loginResponse));
}, failure: (error) {
emit(LoginState.error(
error: error.apiErrorModel.message ?? 'Something went wrong'));
});
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import 'package:doctorito/core/network/api_error_handler.dart';
import 'package:doctorito/features/auth/data/models/login_response.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'login_state.freezed.dart';
Expand All @@ -9,5 +8,5 @@ class LoginState with _$LoginState {
const factory LoginState.loading() = Loading;
const factory LoginState.success({required LoginResponse loginResponse}) =
Success;
const factory LoginState.error({required ErrorHandler error}) = Error;
const factory LoginState.error({required String error}) = Error;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ mixin _$LoginState {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(LoginResponse loginResponse) success,
required TResult Function(ErrorHandler error) error,
required TResult Function(String error) error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(LoginResponse loginResponse)? success,
TResult? Function(ErrorHandler error)? error,
TResult? Function(String error)? error,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? loading,
TResult Function(LoginResponse loginResponse)? success,
TResult Function(ErrorHandler error)? error,
TResult Function(String error)? error,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
Expand Down Expand Up @@ -127,7 +127,7 @@ class _$InitialImpl implements _Initial {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(LoginResponse loginResponse) success,
required TResult Function(ErrorHandler error) error,
required TResult Function(String error) error,
}) {
return initial();
}
Expand All @@ -138,7 +138,7 @@ class _$InitialImpl implements _Initial {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(LoginResponse loginResponse)? success,
TResult? Function(ErrorHandler error)? error,
TResult? Function(String error)? error,
}) {
return initial?.call();
}
Expand All @@ -149,7 +149,7 @@ class _$InitialImpl implements _Initial {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(LoginResponse loginResponse)? success,
TResult Function(ErrorHandler error)? error,
TResult Function(String error)? error,
required TResult orElse(),
}) {
if (initial != null) {
Expand Down Expand Up @@ -241,7 +241,7 @@ class _$LoadingImpl implements Loading {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(LoginResponse loginResponse) success,
required TResult Function(ErrorHandler error) error,
required TResult Function(String error) error,
}) {
return loading();
}
Expand All @@ -252,7 +252,7 @@ class _$LoadingImpl implements Loading {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(LoginResponse loginResponse)? success,
TResult? Function(ErrorHandler error)? error,
TResult? Function(String error)? error,
}) {
return loading?.call();
}
Expand All @@ -263,7 +263,7 @@ class _$LoadingImpl implements Loading {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(LoginResponse loginResponse)? success,
TResult Function(ErrorHandler error)? error,
TResult Function(String error)? error,
required TResult orElse(),
}) {
if (loading != null) {
Expand Down Expand Up @@ -382,7 +382,7 @@ class _$SuccessImpl implements Success {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(LoginResponse loginResponse) success,
required TResult Function(ErrorHandler error) error,
required TResult Function(String error) error,
}) {
return success(loginResponse);
}
Expand All @@ -393,7 +393,7 @@ class _$SuccessImpl implements Success {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(LoginResponse loginResponse)? success,
TResult? Function(ErrorHandler error)? error,
TResult? Function(String error)? error,
}) {
return success?.call(loginResponse);
}
Expand All @@ -404,7 +404,7 @@ class _$SuccessImpl implements Success {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(LoginResponse loginResponse)? success,
TResult Function(ErrorHandler error)? error,
TResult Function(String error)? error,
required TResult orElse(),
}) {
if (success != null) {
Expand Down Expand Up @@ -467,7 +467,7 @@ abstract class _$$ErrorImplCopyWith<$Res> {
_$ErrorImpl value, $Res Function(_$ErrorImpl) then) =
__$$ErrorImplCopyWithImpl<$Res>;
@useResult
$Res call({ErrorHandler error});
$Res call({String error});
}

/// @nodoc
Expand All @@ -487,7 +487,7 @@ class __$$ErrorImplCopyWithImpl<$Res>
error: null == error
? _value.error
: error // ignore: cast_nullable_to_non_nullable
as ErrorHandler,
as String,
));
}
}
Expand All @@ -498,7 +498,7 @@ class _$ErrorImpl implements Error {
const _$ErrorImpl({required this.error});

@override
final ErrorHandler error;
final String error;

@override
String toString() {
Expand Down Expand Up @@ -528,7 +528,7 @@ class _$ErrorImpl implements Error {
required TResult Function() initial,
required TResult Function() loading,
required TResult Function(LoginResponse loginResponse) success,
required TResult Function(ErrorHandler error) error,
required TResult Function(String error) error,
}) {
return error(this.error);
}
Expand All @@ -539,7 +539,7 @@ class _$ErrorImpl implements Error {
TResult? Function()? initial,
TResult? Function()? loading,
TResult? Function(LoginResponse loginResponse)? success,
TResult? Function(ErrorHandler error)? error,
TResult? Function(String error)? error,
}) {
return error?.call(this.error);
}
Expand All @@ -550,7 +550,7 @@ class _$ErrorImpl implements Error {
TResult Function()? initial,
TResult Function()? loading,
TResult Function(LoginResponse loginResponse)? success,
TResult Function(ErrorHandler error)? error,
TResult Function(String error)? error,
required TResult orElse(),
}) {
if (error != null) {
Expand Down Expand Up @@ -598,9 +598,9 @@ class _$ErrorImpl implements Error {
}

abstract class Error implements LoginState {
const factory Error({required final ErrorHandler error}) = _$ErrorImpl;
const factory Error({required final String error}) = _$ErrorImpl;

ErrorHandler get error;
String get error;
@JsonKey(ignore: true)
_$$ErrorImplCopyWith<_$ErrorImpl> get copyWith =>
throw _privateConstructorUsedError;
Expand Down
71 changes: 69 additions & 2 deletions lib/features/auth/presentation/views/login_view.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,78 @@
import 'package:doctorito/features/auth/presentation/views/widgets/login_view_body.dart';
import 'package:doctorito/core/theme/styles.dart';
import 'package:doctorito/features/auth/data/models/login_request_body.dart';
import 'package:doctorito/features/auth/presentation/view_model/login/login_cubit.dart';
import 'package:doctorito/features/auth/presentation/views/widgets/email_and_password.dart';
import 'package:doctorito/features/auth/presentation/views/widgets/login_bloc_listener.dart';
import 'package:doctorito/features/auth/presentation/views/widgets/remember_me.dart';
import 'package:doctorito/features/auth/presentation/views/widgets/terms_and_conditions.dart';
import 'package:doctorito/features/onboarding/presentation/views/widgets/custom_button.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class LoginView extends StatelessWidget {
const LoginView({super.key});

@override
Widget build(BuildContext context) {
return const Scaffold(body: SafeArea(child: LoginViewBody()));
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Welcome Back",
style: Styles.style24PrimaryColorw700,
),
Text(
"We're excited to have you back, can't wait to see what you've been up to since you last logged in.",
style: Styles.style15rGreyw400,
),
SizedBox(
height: 28.h,
),
const EmailAndPassword(),
const RememberMe(),
SizedBox(
height: 24.h,
),
CustomButton(
text: "Login",
onPressed: () {
context.read<LoginCubit>().login(LoginRequestBody(
email: context.read<LoginCubit>().emailController.text,
password:
context.read<LoginCubit>().passwordController.text));
},
),
SizedBox(
height: 10.h,
),
const TermsAndConditions(),
SizedBox(
height: 24.h,
),
Row(
children: [
const Spacer(),
Text.rich(
textAlign: TextAlign.center,
style: Styles.style12Grey3w400,
TextSpan(children: [
const TextSpan(text: "Don't have an account yet? "),
TextSpan(
text: "Sign Up", style: Styles.style12PrimaryColorw600),
]),
),
const Spacer(),
],
),
const LoginBlocListener()
],
),
))));
}
}
Loading

0 comments on commit 765e737

Please sign in to comment.