From f7db91f21281da72db7a539d94e0177949e138e8 Mon Sep 17 00:00:00 2001 From: Abdullah Alassaf Date: Thu, 23 May 2024 14:08:40 +0300 Subject: [PATCH] Added sign up screen and model --- lib/features/auth/bloc/auth_cubit.dart | 30 +++ lib/features/auth/model/signup_model.dart | 29 +++ .../auth/view/widgets/login/login_form.dart | 6 +- .../view/widgets/sign_up/sign_up_view.dart | 208 +++++++++++++++++- .../view/widgets/smart_door/door_button.dart | 11 +- lib/services/api/authentication_api.dart | 13 +- 6 files changed, 285 insertions(+), 12 deletions(-) create mode 100644 lib/features/auth/model/signup_model.dart diff --git a/lib/features/auth/bloc/auth_cubit.dart b/lib/features/auth/bloc/auth_cubit.dart index c084048..3a04300 100644 --- a/lib/features/auth/bloc/auth_cubit.dart +++ b/lib/features/auth/bloc/auth_cubit.dart @@ -1,7 +1,9 @@ +import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:syncrow_app/features/auth/model/login_with_email_model.dart'; +import 'package:syncrow_app/features/auth/model/signup_model.dart'; import 'package:syncrow_app/features/auth/model/token.dart'; import 'package:syncrow_app/features/auth/model/user_model.dart'; import 'package:syncrow_app/navigation/navigation_service.dart'; @@ -16,7 +18,11 @@ class AuthCubit extends Cubit { final TextEditingController emailController = TextEditingController(); final TextEditingController passwordController = TextEditingController(); + + final TextEditingController emailSignUpController = TextEditingController(); + final TextEditingController passwordSignUpController = TextEditingController(); final loginFormKey = GlobalKey(); + final signUpFormKey = GlobalKey(); bool isPasswordVisible = false; static GlobalKey formKey = GlobalKey(); @@ -118,6 +124,30 @@ class AuthCubit extends Cubit { } } + signUp() async { + emit(AuthLoginLoading()); + final response; + try { + response = await AuthenticationAPI.signUp( + model: SignUpModel( + email: emailController.text.toLowerCase(), + password: passwordController.text, + firstName: '', + lastName: ''), + ); + } catch (failure) { + emit(AuthLoginError(message: failure.toString())); + return; + } + if (response['statusCode'] == 201) { + emailController.clear(); + passwordController.clear(); + emit(AuthLoginSuccess()); + } else { + emit(AuthLoginError(message: 'Something went wrong')); + } + } + logout() async { emit(AuthLogoutLoading()); try { diff --git a/lib/features/auth/model/signup_model.dart b/lib/features/auth/model/signup_model.dart new file mode 100644 index 0000000..c4a0adf --- /dev/null +++ b/lib/features/auth/model/signup_model.dart @@ -0,0 +1,29 @@ +class SignUpModel { + final String email; + final String password; + final String firstName; + final String lastName; + + SignUpModel( + {required this.email, + required this.password, + required this.firstName, + required this.lastName}); + + factory SignUpModel.fromJson(Map json) { + return SignUpModel( + email: json['email'], + password: json['password'], + firstName: json['firstName'], + lastName: json['lastName']); + } + + Map toJson() { + return { + 'email': email, + 'password': password, + 'firstName': firstName, + 'lastName': lastName, + }; + } +} diff --git a/lib/features/auth/view/widgets/login/login_form.dart b/lib/features/auth/view/widgets/login/login_form.dart index 5296e23..2b6474d 100644 --- a/lib/features/auth/view/widgets/login/login_form.dart +++ b/lib/features/auth/view/widgets/login/login_form.dart @@ -46,8 +46,7 @@ class LoginForm extends StatelessWidget { FocusScope.of(context).unfocus(); }, onChanged: (value) {}, - decoration: defaultInputDecoration(context, - hint: "Example@email.com"), + decoration: defaultInputDecoration(context, hint: "Example@email.com"), ), const SizedBox(height: 10), const BodyMedium( @@ -72,8 +71,7 @@ class LoginForm extends StatelessWidget { FocusScope.of(context).unfocus(); }, obscureText: !AuthCubit.get(context).isPasswordVisible, - decoration: defaultInputDecoration(context, - hint: "At least 8 characters"), + decoration: defaultInputDecoration(context, hint: "At least 8 characters"), ), const SizedBox(height: 10), // const LoginUserAgreement(), diff --git a/lib/features/auth/view/widgets/sign_up/sign_up_view.dart b/lib/features/auth/view/widgets/sign_up/sign_up_view.dart index ddd94b2..e749db2 100644 --- a/lib/features/auth/view/widgets/sign_up/sign_up_view.dart +++ b/lib/features/auth/view/widgets/sign_up/sign_up_view.dart @@ -1,10 +1,216 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:syncrow_app/features/auth/bloc/auth_cubit.dart'; +import 'package:syncrow_app/features/auth/view/widgets/login/login_form.dart'; +import 'package:syncrow_app/features/shared_widgets/default_button.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/body_medium.dart'; +import 'package:syncrow_app/features/shared_widgets/text_widgets/title_medium.dart'; +import 'package:syncrow_app/generated/assets.dart'; +import 'package:syncrow_app/navigation/routing_constants.dart'; +import 'package:syncrow_app/utils/context_extension.dart'; +import 'package:syncrow_app/utils/resource_manager/constants.dart'; +import 'package:syncrow_app/utils/resource_manager/font_manager.dart'; +import 'package:syncrow_app/utils/resource_manager/styles_manager.dart'; class SignUpView extends StatelessWidget { const SignUpView({super.key}); @override Widget build(BuildContext context) { - return const Placeholder(); + final formKey = AuthCubit.get(context).signUpFormKey; + return BlocConsumer( + listener: (context, state) { + if (state is AuthError) { + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // content: Text(state.message), + // ), + // ); + } else if (state is AuthLoginSuccess) { + Navigator.popAndPushNamed(context, Routes.homeRoute); + } + }, + builder: (context, state) { + return SafeArea( + child: Scaffold( + body: Stack( + children: [ + Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage( + Assets.assetsImagesBackground, + ), + fit: BoxFit.cover, + ), + ), + ), + Container( + width: MediaQuery.sizeOf(context).width, + height: MediaQuery.sizeOf(context).height, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage(Assets.assetsImagesVector), + fit: BoxFit.cover, + opacity: 0.9, + ), + ), + ), + Padding( + padding: const EdgeInsets.only( + right: Constants.defaultPadding, + left: Constants.defaultPadding, + top: Constants.defaultPadding, + ), + child: SingleChildScrollView( + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Center( + child: SvgPicture.asset( + Assets.assetsImagesLogo, + width: 160, + ), + ), + const SizedBox( + height: 40, + ), + TitleMedium( + text: 'Create new account', + style: context.titleMedium.copyWith( + fontWeight: FontsManager.extraBold, + color: Colors.white, + ), + ), + const SizedBox( + height: 20, + ), + Form( + key: formKey, + autovalidateMode: AutovalidateMode.onUserInteraction, + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const BodyMedium( + text: "Email", + fontColor: Colors.white, + ), + TextFormField( + autovalidateMode: AutovalidateMode.disabled, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.text, + scrollPadding: EdgeInsets.zero, + autocorrect: false, + autofillHints: const [AutofillHints.email], + controller: AuthCubit.get(context).emailController, + validator: (value) { + return AuthCubit.get(context).emailAddressValidator(value); + }, + onTapOutside: (event) { + FocusScope.of(context).unfocus(); + }, + onChanged: (value) {}, + decoration: + defaultInputDecoration(context, hint: "Example@email.com"), + ), + const SizedBox(height: 15), + const BodyMedium( + text: "Password", + fontColor: Colors.white, + ), + TextFormField( + autovalidateMode: AutovalidateMode.disabled, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.text, + scrollPadding: EdgeInsets.zero, + autocorrect: false, + autofillHints: const [AutofillHints.password], + controller: AuthCubit.get(context).passwordController, + validator: (value) { + return AuthCubit.get(context).passwordValidator(value); + }, + onTapOutside: (event) { + FocusScope.of(context).unfocus(); + }, + obscureText: !AuthCubit.get(context).isPasswordVisible, + decoration: defaultInputDecoration(context, + hint: "At least 8 characters"), + ), + const SizedBox(height: 15), + const BodyMedium( + text: "Re-enter Password", + fontColor: Colors.white, + ), + TextFormField( + autovalidateMode: AutovalidateMode.disabled, + textInputAction: TextInputAction.done, + keyboardType: TextInputType.text, + scrollPadding: EdgeInsets.zero, + autocorrect: false, + autofillHints: const [AutofillHints.password], + controller: AuthCubit.get(context).passwordController, + validator: (value) { + return AuthCubit.get(context).passwordValidator(value); + }, + onTapOutside: (event) { + FocusScope.of(context).unfocus(); + }, + obscureText: !AuthCubit.get(context).isPasswordVisible, + decoration: defaultInputDecoration(context, + hint: "At least 8 characters"), + ), + const SizedBox(height: 40), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: DefaultButton( + isDone: state is AuthLoginSuccess, + isLoading: state is AuthLoading, + customButtonStyle: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + Colors.black.withOpacity(.25), + ), + foregroundColor: MaterialStateProperty.all( + Colors.white, + ), + ), + child: const Text( + 'Sign up', + ), + onPressed: () { + if (formKey.currentState!.validate()) { + if ((state is! AuthLoading)) { + AuthCubit.get(context).login(); + FocusScope.of(context).unfocus(); + } + } + }, + ), + ), + ], + ) + ], + ), + ), + ), + ], + ), + ), + ), + ) + ], + ), + ), + ); + }, + ); + ; } } diff --git a/lib/features/devices/view/widgets/smart_door/door_button.dart b/lib/features/devices/view/widgets/smart_door/door_button.dart index 08339ed..fd250d5 100644 --- a/lib/features/devices/view/widgets/smart_door/door_button.dart +++ b/lib/features/devices/view/widgets/smart_door/door_button.dart @@ -53,6 +53,7 @@ class _DoorLockButtonState extends State with SingleTickerProvid // } BlocProvider.of(context) .add(UpdateLockEvent(value: smartDoorModel.normalOpenSwitch)); + _animationController.reverse(); } else if (_animation.status == AnimationStatus.dismissed) { // if (widget.doorLock.status // .firstWhere((element) => element.code == 'normal_open_switch') @@ -98,11 +99,11 @@ class _DoorLockButtonState extends State with SingleTickerProvid } }, onTapUp: (details) { - if (_animationController.status == AnimationStatus.forward) { - _animationController.reverse(); - } else if (_animationController.status == AnimationStatus.reverse) { - _animationController.forward(); - } + // if (_animationController.status == AnimationStatus.forward) { + // _animationController.reverse(); + // } else if (_animationController.status == AnimationStatus.reverse) { + // _animationController.forward(); + // } }, child: Container( width: context.width * 06, diff --git a/lib/services/api/authentication_api.dart b/lib/services/api/authentication_api.dart index a5e81e4..71d4f32 100644 --- a/lib/services/api/authentication_api.dart +++ b/lib/services/api/authentication_api.dart @@ -1,4 +1,5 @@ import 'package:syncrow_app/features/auth/model/login_with_email_model.dart'; +import 'package:syncrow_app/features/auth/model/signup_model.dart'; import 'package:syncrow_app/features/auth/model/token.dart'; import 'package:syncrow_app/features/auth/model/verify_code.dart'; import 'package:syncrow_app/services/api/api_links_endpoints.dart'; @@ -14,8 +15,7 @@ class AuthenticationAPI { return response; } - static Future loginWithEmail( - {required LoginWithEmailModel model}) async { + static Future loginWithEmail({required LoginWithEmailModel model}) async { final response = await HTTPService().post( path: ApiEndpoints.login, body: model.toJson(), @@ -23,4 +23,13 @@ class AuthenticationAPI { expectedResponseModel: (json) => Token.fromJson(json['data'])); return response; } + + static Future signUp({required SignUpModel model}) async { + final response = await HTTPService().post( + path: ApiEndpoints.signUp, + body: model.toJson(), + showServerMessage: false, + expectedResponseModel: (json) => Token.fromJson(json['data'])); + return response; + } }