From 288518f12a763e790393aceadea3de4de8eababc Mon Sep 17 00:00:00 2001 From: maxxkim Date: Tue, 9 Jan 2024 14:09:29 +0300 Subject: [PATCH] Auth screen layout --- lib/localization/ru/ru_translations.dart | 5 + .../auth/auth_screen/auth_screen.dart | 139 ++++++++++++++++++ .../auth/auth_screen/bloc/auth_bloc.dart | 22 +++ .../auth/auth_screen/bloc/auth_event.dart | 16 ++ .../auth/auth_screen/bloc/auth_state.dart | 36 +++++ .../auth/auth_screen/models/auth_model.dart | 16 ++ .../additional_information_screen.dart | 4 + .../app_navigation_screen.dart | 6 + .../splash_screen/bloc/splash_bloc.dart | 2 +- lib/routes/app_routes.dart | 4 + 10 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 lib/presentation/auth/auth_screen/auth_screen.dart create mode 100644 lib/presentation/auth/auth_screen/bloc/auth_bloc.dart create mode 100644 lib/presentation/auth/auth_screen/bloc/auth_event.dart create mode 100644 lib/presentation/auth/auth_screen/bloc/auth_state.dart create mode 100644 lib/presentation/auth/auth_screen/models/auth_model.dart diff --git a/lib/localization/ru/ru_translations.dart b/lib/localization/ru/ru_translations.dart index 780dd19f..49b8047d 100644 --- a/lib/localization/ru/ru_translations.dart +++ b/lib/localization/ru/ru_translations.dart @@ -100,6 +100,11 @@ final Map ru = { "pressure_empty": "Все показатели давления обязательны к заполнению", "pressure_is_not_a_number": "Все показатели давления должны быть числами", + // Authorization + "no_account": "У меня нет аккаунта", + "authentificate": "Войти", + "user_authorization": "Аутентификация", + // Личный кабинет Screen "Dmitry_Koval": "Дмитрий Коваль", "done": "Готово", "exit": "Выйти", "my_blog": "Мой блог", "lbl_01_04_21": "01.04.21", "lbl_01_05_21": "01.05.21", diff --git a/lib/presentation/auth/auth_screen/auth_screen.dart b/lib/presentation/auth/auth_screen/auth_screen.dart new file mode 100644 index 00000000..aafd1c79 --- /dev/null +++ b/lib/presentation/auth/auth_screen/auth_screen.dart @@ -0,0 +1,139 @@ +import 'bloc/auth_bloc.dart'; +import 'models/auth_model.dart'; +import 'package:eatvio_app/core/app_export.dart'; +import 'package:eatvio_app/widgets/app_bar/appbar_title_image.dart'; +import 'package:eatvio_app/widgets/app_bar/custom_app_bar.dart'; +import 'package:validators/validators.dart'; +import 'package:eatvio_app/widgets/next_step_button.dart'; +import 'package:eatvio_app/widgets/custom_text_form_field.dart'; +import 'package:flutter/material.dart'; + +class AuthScreen extends StatelessWidget { + static Widget builder(BuildContext context) { + return BlocProvider( + create: (context) => AuthBloc( + AuthState( + authModelObj: AuthModel(), + ), + )..add(AuthInitialEvent()), + child: AuthScreen(), + ); + } + + @override + Widget build(BuildContext context) { + mediaQueryData = MediaQuery.of(context); + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: _buildAppBar(context), + body: Container( + width: double.maxFinite, + padding: EdgeInsets.symmetric(horizontal: 16.h, vertical: 9.v), + child: Column(children: [ + SizedBox(height: 39.v), + Text("user_authorization".tr, + style: theme.textTheme.headlineSmall), + SizedBox(height: 60.v), + Text("email".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 3.v), + BlocSelector( + selector: (state) => state.emailController, + builder: (context, emailController) { + return CustomTextFormField( + controller: emailController, + textInputAction: TextInputAction.done); + }), + SizedBox(height: 22.v), + Text("password".tr, style: theme.textTheme.bodyLarge), + SizedBox(height: 5.v), + BlocSelector( + selector: (state) => state.passwordController, + builder: (context, passwordController) { + return CustomTextFormField( + obscureText: true, + controller: passwordController, + ); + }), + Spacer(), + NextStepButton( + text: "no_account".tr.toUpperCase(), + margin: + EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), + buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + onPressed: () { + Navigator.pushNamed( + context, AppRoutes.languageSelectScreen); + }), + BlocBuilder(builder: (context, state) { + return NextStepButton( + text: "authentificate".tr.toUpperCase(), + margin: + EdgeInsets.only(left: 16.h, right: 16.h, bottom: 50.v), + buttonStyle: CustomButtonStyles.outlinePrimaryTL15, + onPressed: () { + List errors = validateFields( + state.emailController!.text, + state.passwordController!.text); + if (errors.isEmpty) { + Navigator.pushNamed( + context, + AppRoutes.myProfile, + ); + } else { + String errorMessage = ''; + + for (var error in errors) { + errorMessage += error + '\n\n'; + } + + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text('Ошибка'), + content: Text(errorMessage), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context); + }, + child: Text('OK'), + ), + ], + ), + ); + } + }); + }), + ]))); + } + + /// Section Widget + PreferredSizeWidget _buildAppBar(BuildContext context) { + return CustomAppBar( + leadingWidth: 29.h, + centerTitle: true, + title: AppbarTitleImage(imagePath: ImageConstant.imgLogoEatvio)); + } + + List validateFields(String email, String password) { + List errors = []; + + if (!isEmail(email)) { + errors.add('email_invalid'.tr); + } + + if (email.isEmpty) { + errors.add('email_empty'.tr); + } + + if (password.isEmpty) { + errors.add('password_empty'.tr); + } + + if (password.length < 6) { + errors.add('password_short'.tr); + } + + return errors; + } +} diff --git a/lib/presentation/auth/auth_screen/bloc/auth_bloc.dart b/lib/presentation/auth/auth_screen/bloc/auth_bloc.dart new file mode 100644 index 00000000..cd0203ac --- /dev/null +++ b/lib/presentation/auth/auth_screen/bloc/auth_bloc.dart @@ -0,0 +1,22 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import '/core/app_export.dart'; +import 'package:eatvio_app/presentation/auth/auth_screen/models/auth_model.dart'; +part 'auth_event.dart'; +part 'auth_state.dart'; + +/// A bloc that manages the state of a Auth according to the event that is dispatched to it. +class AuthBloc extends Bloc { + AuthBloc(AuthState initialState) : super(initialState) { + on(_onInitialize); + } + + _onInitialize( + AuthInitialEvent event, + Emitter emit, + ) async { + emit(state.copyWith( + emailController: TextEditingController(), + passwordController: TextEditingController())); + } +} diff --git a/lib/presentation/auth/auth_screen/bloc/auth_event.dart b/lib/presentation/auth/auth_screen/bloc/auth_event.dart new file mode 100644 index 00000000..a395a45b --- /dev/null +++ b/lib/presentation/auth/auth_screen/bloc/auth_event.dart @@ -0,0 +1,16 @@ +// ignore_for_file: must_be_immutable + +part of 'auth_bloc.dart'; + +/// Abstract class for all events that can be dispatched from the +///Credentials widget. +/// +/// Events must be immutable and implement the [Equatable] interface. +@immutable +abstract class AuthEvent extends Equatable {} + +/// Event that is dispatched when the Credentials widget is first created. +class AuthInitialEvent extends AuthEvent { + @override + List get props => []; +} diff --git a/lib/presentation/auth/auth_screen/bloc/auth_state.dart b/lib/presentation/auth/auth_screen/bloc/auth_state.dart new file mode 100644 index 00000000..504180f0 --- /dev/null +++ b/lib/presentation/auth/auth_screen/bloc/auth_state.dart @@ -0,0 +1,36 @@ +// ignore_for_file: must_be_immutable + +part of 'auth_bloc.dart'; + +/// Represents the state of Auth in the application. +class AuthState extends Equatable { + AuthState({ + this.emailController, + this.passwordController, + this.authModelObj, + }); + + TextEditingController? emailController; + + TextEditingController? passwordController; + + AuthModel? authModelObj; + + @override + List get props => [ + emailController, + passwordController, + authModelObj, + ]; + AuthState copyWith({ + TextEditingController? emailController, + TextEditingController? passwordController, + AuthModel? authModelObj, + }) { + return AuthState( + emailController: emailController ?? this.emailController, + passwordController: passwordController ?? this.passwordController, + authModelObj: authModelObj ?? this.authModelObj, + ); + } +} diff --git a/lib/presentation/auth/auth_screen/models/auth_model.dart b/lib/presentation/auth/auth_screen/models/auth_model.dart new file mode 100644 index 00000000..829b38c8 --- /dev/null +++ b/lib/presentation/auth/auth_screen/models/auth_model.dart @@ -0,0 +1,16 @@ +// ignore_for_file: must_be_immutable + +import 'package:equatable/equatable.dart'; + +/// This class defines the variables used in the [auth_screen], +/// and is typically used to hold data that is passed between different parts of the application. +class AuthModel extends Equatable { + AuthModel(); + + AuthModel copyWith() { + return AuthModel(); + } + + @override + List get props => []; +} diff --git a/lib/presentation/registration/additional_information_screen/additional_information_screen.dart b/lib/presentation/registration/additional_information_screen/additional_information_screen.dart index a5783af4..34fe4bf2 100644 --- a/lib/presentation/registration/additional_information_screen/additional_information_screen.dart +++ b/lib/presentation/registration/additional_information_screen/additional_information_screen.dart @@ -80,6 +80,10 @@ class AdditionalInformationScreen extends StatelessWidget { final token = json.decode(response.body)['token']; box.put('token', token); print('token: ${box.get('token')}'); + Navigator.pushNamed( + context, + AppRoutes.myProfile, + ); }); }), ])))); diff --git a/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart b/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart index 0a485d18..5dd89c25 100644 --- a/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart +++ b/lib/presentation/registration/app_navigation_screen/app_navigation_screen.dart @@ -130,6 +130,12 @@ class _AppNavigationScreenState extends State { onTapScreenTitle: () => onTapScreenTitle( AppRoutes.languageSelectScreen), ), + _buildScreenTitle( + context, + screenTitle: "auth".tr, + onTapScreenTitle: () => + onTapScreenTitle(AppRoutes.authScreen), + ), ], ), ), diff --git a/lib/presentation/registration/splash_screen/bloc/splash_bloc.dart b/lib/presentation/registration/splash_screen/bloc/splash_bloc.dart index af1fe470..a767f763 100644 --- a/lib/presentation/registration/splash_screen/bloc/splash_bloc.dart +++ b/lib/presentation/registration/splash_screen/bloc/splash_bloc.dart @@ -17,7 +17,7 @@ class SplashBloc extends Bloc { ) async { Future.delayed(const Duration(milliseconds: 3000), () { NavigatorService.popAndPushNamed( - AppRoutes.languageSelectScreen, + AppRoutes.authScreen, ); }); } diff --git a/lib/routes/app_routes.dart b/lib/routes/app_routes.dart index 02b1736d..147c6b29 100644 --- a/lib/routes/app_routes.dart +++ b/lib/routes/app_routes.dart @@ -16,6 +16,7 @@ import 'package:eatvio_app/presentation/registration/height_screen/height_screen import 'package:eatvio_app/presentation/registration/credentials_screen/credentials_screen.dart'; import 'package:eatvio_app/presentation/registration/name_screen/name_screen.dart'; import 'package:eatvio_app/presentation/registration/app_navigation_screen/app_navigation_screen.dart'; +import 'package:eatvio_app/presentation/auth/auth_screen/auth_screen.dart'; import 'package:eatvio_app/presentation/profile/my_profile_screen/my_profile_screen.dart'; class AppRoutes { @@ -58,6 +59,8 @@ class AppRoutes { static const String appNavigationScreen = '/app_navigation_screen'; + static const String authScreen = '/auth_screen'; + static const String initialRoute = '/initialRoute'; //PROFILE @@ -74,6 +77,7 @@ class AppRoutes { allergiesScreen: AllergiesScreen.builder, medicineIntakeScreen: MedicineIntakeScreen.builder, splashScreen: SplashScreen.builder, + authScreen: AuthScreen.builder, genderScreen: GenderScreen.builder, bloodPressureScreen: BloodPressureScreen.builder, favoriteSportsScreen: FavoriteSportsScreen.builder,