From ece15635eb1ceffc9471c5cfc622736a727bf054 Mon Sep 17 00:00:00 2001 From: jahongireshonqulov Date: Wed, 29 Oct 2025 15:16:44 +0500 Subject: [PATCH] feat:register page ui done --- assets/translations/app_en.arb | 14 +- assets/translations/app_ru.arb | 14 +- assets/translations/app_uz.arb | 14 +- lib/core/helpers/validator_helpers.dart | 28 ++ lib/core/l10n/app_localizations.dart | 98 +++++-- lib/core/l10n/app_localizations_en.dart | 36 ++- lib/core/l10n/app_localizations_ru.dart | 54 +++- lib/core/l10n/app_localizations_uz.dart | 55 +++- lib/core/theme/app_colors.dart | 1 + lib/core/utils/app_utils.dart | 6 + .../blocs/login_bloc/login_bloc.dart | 2 + .../pages/login_page/login_page.dart | 9 +- .../pages/login_page/widgets/login_body.dart | 242 +++++++++--------- .../pages/register_page/register_page.dart | 6 +- .../widgets/w_register_body.dart | 205 +++++++++++++++ .../widgets/w_auth_background.dart | 35 +++ .../widgets/app_text_form_field.dart | 125 ++++----- .../widgets/w_toastification.dart | 5 +- 18 files changed, 708 insertions(+), 241 deletions(-) create mode 100644 lib/core/helpers/validator_helpers.dart create mode 100644 lib/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart create mode 100644 lib/feature/auth/presentation/widgets/w_auth_background.dart diff --git a/assets/translations/app_en.arb b/assets/translations/app_en.arb index e9065e7..57e9fcb 100644 --- a/assets/translations/app_en.arb +++ b/assets/translations/app_en.arb @@ -163,9 +163,19 @@ "password": "Password", "enter_password": "Enter password", "forgot_password": "Forgot password", - "Continue": "Continue", + "continue_str": "Continue", "dont_have_account": "Don't have an account?", - "sign_up": "SignUp" + "sign_up": "SignUp", + "field_cannot_be_empty": "This field cannot be empty", + "password_too_short": "Password must be at least 6 characters long", + "invalid_phone_format": "Invalid phone number format", + "first_name": "First name", + "enter_first_name": "Enter first name", + "repeat_password": "Repeat password", + "enter_repeat_password": "Enter repeat password", + "already_has_account": "Already have an account?", + "unexpected_error": "Unexpected error" + diff --git a/assets/translations/app_ru.arb b/assets/translations/app_ru.arb index a8c8f51..e4384b7 100644 --- a/assets/translations/app_ru.arb +++ b/assets/translations/app_ru.arb @@ -158,9 +158,19 @@ "password": "Пароль", "enter_password": "Введите пароль", "forgot_password": "Забыли пароль", - "Continue": "Продолжить", + "continue_str": "Продолжить", "dont_have_account": "У вас нет аккаунта?", - "sign_up": "Зарегистрироваться" + "sign_up": "Зарегистрироваться", + "field_cannot_be_empty": "Это поле не может быть пустым", + "password_too_short": "Пароль не может быть короче 6 символов", + "invalid_phone_format": "Неверный формат номера телефона", + "first_name": "Имя", + "enter_first_name": "Введите имя", + "repeat_password": "Повторите пароль", + "enter_repeat_password": "Введите пароль ещё раз", + "already_has_account": "Уже есть аккаунт?", + "unexpected_error": "Неожиданная ошибка" + diff --git a/assets/translations/app_uz.arb b/assets/translations/app_uz.arb index 4d32f82..3477e47 100644 --- a/assets/translations/app_uz.arb +++ b/assets/translations/app_uz.arb @@ -158,9 +158,19 @@ "password": "Parol", "enter_password": "Parolni kiriting", "forgot_password": "Parolni unutdingizmi", - "Continue": "Davom etish", + "continue_str": "Davom etish", "dont_have_account": "Hisobingiz yo‘qmi?", - "sign_up": "Ro‘yxatdan o‘tish" + "sign_up": "Ro‘yxatdan o‘tish", + "field_cannot_be_empty": "Bu maydon bo'sh bo'lishi mumkin emas", + "password_too_short": "Parol 6 ta belgidan kam bo'lishi mumkin emas", + "invalid_phone_format": "Noto'g'ri telefon raqam formati", + "first_name": "Ism", + "enter_first_name": "Ismingizni kiriting", + "repeat_password": "Parolni takrorlang", + "enter_repeat_password": "Parolni qayta kiriting", + "already_has_account": "Akkountingiz bormi?", + "unexpected_error": "Kutilmagan xatolik" + diff --git a/lib/core/helpers/validator_helpers.dart b/lib/core/helpers/validator_helpers.dart new file mode 100644 index 0000000..0809196 --- /dev/null +++ b/lib/core/helpers/validator_helpers.dart @@ -0,0 +1,28 @@ +import 'package:food_delivery_client/core/core.dart'; + +abstract class Validators { + static String? validatePhoneNumber(String phoneNumber) { + if (phoneNumber.isEmpty) { + return navigatorKey.currentContext?.loc.field_cannot_be_empty; + } else if (phoneNumber.length < 12) { + return navigatorKey.currentContext?.loc.invalid_phone_format; + } + return null; + } + + static String? validatePassword(String password) { + if (password.isEmpty) { + return navigatorKey.currentContext?.loc.field_cannot_be_empty; + } else if (password.length < 6) { + return navigatorKey.currentContext?.loc.password_too_short; + } + return null; + } + + static String? validateFields(String value) { + if (value.isEmpty) { + return navigatorKey.currentContext?.loc.field_cannot_be_empty; + } + return null; + } +} diff --git a/lib/core/l10n/app_localizations.dart b/lib/core/l10n/app_localizations.dart index 9f8ce3a..eec988d 100644 --- a/lib/core/l10n/app_localizations.dart +++ b/lib/core/l10n/app_localizations.dart @@ -63,7 +63,8 @@ import 'app_localizations_uz.dart'; /// be consistent with the languages listed in the AppLocalizations.supportedLocales /// property. abstract class AppLocalizations { - AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -71,7 +72,8 @@ abstract class AppLocalizations { return Localizations.of(context, AppLocalizations); } - static const LocalizationsDelegate delegate = _AppLocalizationsDelegate(); + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); /// A list of this localizations delegate along with the default localizations /// delegates. @@ -83,18 +85,19 @@ abstract class AppLocalizations { /// Additional delegates can be added by appending to this list in /// MaterialApp. This list does not have to be used at all if a custom list /// of delegates is preferred or required. - static const List> localizationsDelegates = >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ Locale('en'), Locale('ru'), - Locale('uz') + Locale('uz'), ]; /// No description provided for @useYourTAxiAccount. @@ -787,11 +790,11 @@ abstract class AppLocalizations { /// **'Forgot password'** String get forgot_password; - /// No description provided for @continue. + /// No description provided for @continue_str. /// /// In en, this message translates to: /// **'Continue'** - String get Continue; + String get continue_str; /// No description provided for @dont_have_account. /// @@ -804,9 +807,64 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'SignUp'** String get sign_up; + + /// No description provided for @field_cannot_be_empty. + /// + /// In en, this message translates to: + /// **'This field cannot be empty'** + String get field_cannot_be_empty; + + /// No description provided for @password_too_short. + /// + /// In en, this message translates to: + /// **'Password must be at least 6 characters long'** + String get password_too_short; + + /// No description provided for @invalid_phone_format. + /// + /// In en, this message translates to: + /// **'Invalid phone number format'** + String get invalid_phone_format; + + /// No description provided for @first_name. + /// + /// In en, this message translates to: + /// **'First name'** + String get first_name; + + /// No description provided for @enter_first_name. + /// + /// In en, this message translates to: + /// **'Enter first name'** + String get enter_first_name; + + /// No description provided for @repeat_password. + /// + /// In en, this message translates to: + /// **'Repeat password'** + String get repeat_password; + + /// No description provided for @enter_repeat_password. + /// + /// In en, this message translates to: + /// **'Enter repeat password'** + String get enter_repeat_password; + + /// No description provided for @already_has_account. + /// + /// In en, this message translates to: + /// **'Already have an account?'** + String get already_has_account; + + /// No description provided for @unexpected_error. + /// + /// In en, this message translates to: + /// **'Unexpected error'** + String get unexpected_error; } -class _AppLocalizationsDelegate extends LocalizationsDelegate { +class _AppLocalizationsDelegate + extends LocalizationsDelegate { const _AppLocalizationsDelegate(); @override @@ -815,26 +873,28 @@ class _AppLocalizationsDelegate extends LocalizationsDelegate } @override - bool isSupported(Locale locale) => ['en', 'ru', 'uz'].contains(locale.languageCode); + bool isSupported(Locale locale) => + ['en', 'ru', 'uz'].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false; } AppLocalizations lookupAppLocalizations(Locale locale) { - - // Lookup logic when only language code is specified. switch (locale.languageCode) { - case 'en': return AppLocalizationsEn(); - case 'ru': return AppLocalizationsRu(); - case 'uz': return AppLocalizationsUz(); + case 'en': + return AppLocalizationsEn(); + case 'ru': + return AppLocalizationsRu(); + case 'uz': + return AppLocalizationsUz(); } throw FlutterError( 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' 'an issue with the localizations generation tool. Please file an issue ' 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.' + 'that was used.', ); } diff --git a/lib/core/l10n/app_localizations_en.dart b/lib/core/l10n/app_localizations_en.dart index 708e9a3..6d45150 100644 --- a/lib/core/l10n/app_localizations_en.dart +++ b/lib/core/l10n/app_localizations_en.dart @@ -21,7 +21,8 @@ class AppLocalizationsEn extends AppLocalizations { String get next => 'Next'; @override - String get contestToGetCallAndSms => 'By proceeding, you consent to get calls, Whatsapp or SMS messages, including by automated means, from uber and its affiliates to the number provided.'; + String get contestToGetCallAndSms => + 'By proceeding, you consent to get calls, Whatsapp or SMS messages, including by automated means, from uber and its affiliates to the number provided.'; @override String get continueWithGoogle => 'Continue with google'; @@ -238,7 +239,8 @@ class AppLocalizationsEn extends AppLocalizations { String get addItemsStartBasket => 'Add items to start a basket'; @override - String get basketHint => 'Once you add items from a restaurant or store, your basket will appear here.'; + String get basketHint => + 'Once you add items from a restaurant or store, your basket will appear here.'; @override String get startShopping => 'Start Shopping'; @@ -364,11 +366,39 @@ class AppLocalizationsEn extends AppLocalizations { String get forgot_password => 'Forgot password'; @override - String get Continue => 'Continue'; + String get continue_str => 'Continue'; @override String get dont_have_account => 'Don\'t have an account?'; @override String get sign_up => 'SignUp'; + + @override + String get field_cannot_be_empty => 'This field cannot be empty'; + + @override + String get password_too_short => + 'Password must be at least 6 characters long'; + + @override + String get invalid_phone_format => 'Invalid phone number format'; + + @override + String get first_name => 'First name'; + + @override + String get enter_first_name => 'Enter first name'; + + @override + String get repeat_password => 'Repeat password'; + + @override + String get enter_repeat_password => 'Enter repeat password'; + + @override + String get already_has_account => 'Already have an account?'; + + @override + String get unexpected_error => 'Unexpected error'; } diff --git a/lib/core/l10n/app_localizations_ru.dart b/lib/core/l10n/app_localizations_ru.dart index a43e030..3f27729 100644 --- a/lib/core/l10n/app_localizations_ru.dart +++ b/lib/core/l10n/app_localizations_ru.dart @@ -9,7 +9,8 @@ class AppLocalizationsRu extends AppLocalizations { AppLocalizationsRu([String locale = 'ru']) : super(locale); @override - String get useYourTAxiAccount => 'Используйте свой аккаунт Uber, чтобы начать'; + String get useYourTAxiAccount => + 'Используйте свой аккаунт Uber, чтобы начать'; @override String get enterYourMobileNumber => 'Введите свой номер телефона'; @@ -21,7 +22,8 @@ class AppLocalizationsRu extends AppLocalizations { String get next => 'Далее'; @override - String get contestToGetCallAndSms => 'Продолжая, вы соглашаетесь получать звонки, сообщения WhatsApp или SMS, включая автоматические, от Uber и его партнеров на указанный номер.'; + String get contestToGetCallAndSms => + 'Продолжая, вы соглашаетесь получать звонки, сообщения WhatsApp или SMS, включая автоматические, от Uber и его партнеров на указанный номер.'; @override String get continueWithGoogle => 'Продолжить через Google'; @@ -238,7 +240,8 @@ class AppLocalizationsRu extends AppLocalizations { String get addItemsStartBasket => 'Добавьте товары, чтобы создать корзину'; @override - String get basketHint => 'Когда вы добавите товары из ресторана или магазина, ваша корзина появится здесь.'; + String get basketHint => + 'Когда вы добавите товары из ресторана или магазина, ваша корзина появится здесь.'; @override String get startShopping => 'Начать покупки'; @@ -346,29 +349,56 @@ class AppLocalizationsRu extends AppLocalizations { String get language => 'Язык'; @override - String get login => 'Login'; + String get login => 'Вход'; @override - String get phone_number => 'Phone number'; + String get phone_number => 'Номер телефона'; @override - String get enter_phone_number => 'Enter phone number'; + String get enter_phone_number => 'Введите номер телефона'; @override - String get password => 'Password'; + String get password => 'Пароль'; @override - String get enter_password => 'Enter password'; + String get enter_password => 'Введите пароль'; @override - String get forgot_password => 'Forgot password'; + String get forgot_password => 'Забыли пароль'; @override - String get Continue => 'Continue'; + String get continue_str => 'Продолжить'; @override - String get dont_have_account => 'Don\'t have an account?'; + String get dont_have_account => 'У вас нет аккаунта?'; @override - String get sign_up => 'SignUp'; + String get sign_up => 'Зарегистрироваться'; + + @override + String get field_cannot_be_empty => 'Это поле не может быть пустым'; + + @override + String get password_too_short => 'Пароль не может быть короче 6 символов'; + + @override + String get invalid_phone_format => 'Неверный формат номера телефона'; + + @override + String get first_name => 'Имя'; + + @override + String get enter_first_name => 'Введите имя'; + + @override + String get repeat_password => 'Повторите пароль'; + + @override + String get enter_repeat_password => 'Введите пароль ещё раз'; + + @override + String get already_has_account => 'Уже есть аккаунт?'; + + @override + String get unexpected_error => 'Неожиданная ошибка'; } diff --git a/lib/core/l10n/app_localizations_uz.dart b/lib/core/l10n/app_localizations_uz.dart index ad38260..c26cc86 100644 --- a/lib/core/l10n/app_localizations_uz.dart +++ b/lib/core/l10n/app_localizations_uz.dart @@ -9,7 +9,8 @@ class AppLocalizationsUz extends AppLocalizations { AppLocalizationsUz([String locale = 'uz']) : super(locale); @override - String get useYourTAxiAccount => 'Boshlash uchun Uber hisobingizdan foydalaning'; + String get useYourTAxiAccount => + 'Boshlash uchun Uber hisobingizdan foydalaning'; @override String get enterYourMobileNumber => 'Telefon raqamingizni kiriting'; @@ -21,7 +22,8 @@ class AppLocalizationsUz extends AppLocalizations { String get next => 'Keyingi'; @override - String get contestToGetCallAndSms => 'Davom etish orqali siz Uber va uning hamkorlaridan avtomatlashtirilgan qo‘ng‘iroqlar, WhatsApp yoki SMS xabarlarini olishga rozilik bildirasiz.'; + String get contestToGetCallAndSms => + 'Davom etish orqali siz Uber va uning hamkorlaridan avtomatlashtirilgan qo‘ng‘iroqlar, WhatsApp yoki SMS xabarlarini olishga rozilik bildirasiz.'; @override String get continueWithGoogle => 'Google orqali davom etish'; @@ -238,7 +240,8 @@ class AppLocalizationsUz extends AppLocalizations { String get addItemsStartBasket => 'Savatni boshlash uchun mahsulot qo‘shing'; @override - String get basketHint => 'Restorandan yoki do\'kondan mahsulot qo‘shsangiz, savatingiz shu yerda paydo bo‘ladi.'; + String get basketHint => + 'Restorandan yoki do\'kondan mahsulot qo‘shsangiz, savatingiz shu yerda paydo bo‘ladi.'; @override String get startShopping => 'Xaridni boshlash'; @@ -346,29 +349,57 @@ class AppLocalizationsUz extends AppLocalizations { String get language => 'Til'; @override - String get login => 'Login'; + String get login => 'Kirish'; @override - String get phone_number => 'Phone number'; + String get phone_number => 'Telefon raqami'; @override - String get enter_phone_number => 'Enter phone number'; + String get enter_phone_number => 'Telefon raqamingizni kiriting'; @override - String get password => 'Password'; + String get password => 'Parol'; @override - String get enter_password => 'Enter password'; + String get enter_password => 'Parolni kiriting'; @override - String get forgot_password => 'Forgot password'; + String get forgot_password => 'Parolni unutdingizmi'; @override - String get Continue => 'Continue'; + String get continue_str => 'Davom etish'; @override - String get dont_have_account => 'Don\'t have an account?'; + String get dont_have_account => 'Hisobingiz yo‘qmi?'; @override - String get sign_up => 'SignUp'; + String get sign_up => 'Ro‘yxatdan o‘tish'; + + @override + String get field_cannot_be_empty => 'Bu maydon bo\'sh bo\'lishi mumkin emas'; + + @override + String get password_too_short => + 'Parol 6 ta belgidan kam bo\'lishi mumkin emas'; + + @override + String get invalid_phone_format => 'Noto\'g\'ri telefon raqam formati'; + + @override + String get first_name => 'Ism'; + + @override + String get enter_first_name => 'Ismingizni kiriting'; + + @override + String get repeat_password => 'Parolni takrorlang'; + + @override + String get enter_repeat_password => 'Parolni qayta kiriting'; + + @override + String get already_has_account => 'Akkountingiz bormi?'; + + @override + String get unexpected_error => 'Kutilmagan xatolik'; } diff --git a/lib/core/theme/app_colors.dart b/lib/core/theme/app_colors.dart index 2f18847..cbc0c35 100644 --- a/lib/core/theme/app_colors.dart +++ b/lib/core/theme/app_colors.dart @@ -25,5 +25,6 @@ abstract class AppColors { static const Color cE8E8E8 = Color(0xFFE8E8E8); static const Color c660000 = Color(0x66000000); static const Color c6A6E7F = Color(0xFF6A6E7F); + static const Color c7F7F7F = Color(0xFF7F7F7F); } diff --git a/lib/core/utils/app_utils.dart b/lib/core/utils/app_utils.dart index 2bef427..ce77d16 100644 --- a/lib/core/utils/app_utils.dart +++ b/lib/core/utils/app_utils.dart @@ -13,6 +13,7 @@ abstract class AppUtils { static const Radius kRadius22 = Radius.circular(22); static const Radius kRadius24 = Radius.circular(24); static const Radius kRadius25 = Radius.circular(25); + static const Radius kRadius30 = Radius.circular(30); static const BorderRadius kBorderRadius = BorderRadius.zero; static const BorderRadius kBorderRadius2 = BorderRadius.all( Radius.circular(2), @@ -92,6 +93,11 @@ abstract class AppUtils { topLeft: kRadius20, topRight: kRadius20, ); + + static const BorderRadius kBorderRadiusTop30 = BorderRadius.only( + topLeft: kRadius30, + topRight: kRadius30, + ); static const BorderRadius kBorderRadiusTop20Bottom20 = BorderRadius.only( bottomRight: kRadius20, topRight: kRadius20, diff --git a/lib/feature/auth/presentation/blocs/login_bloc/login_bloc.dart b/lib/feature/auth/presentation/blocs/login_bloc/login_bloc.dart index 1c2e3b2..80ebcd2 100644 --- a/lib/feature/auth/presentation/blocs/login_bloc/login_bloc.dart +++ b/lib/feature/auth/presentation/blocs/login_bloc/login_bloc.dart @@ -32,6 +32,8 @@ class LoginBloc extends Bloc { (r) { showSuccessToast("Login success"); _storageService.setString(key: AppLocaleKeys.token, value: r.token); + final token = _storageService.getString(key: AppLocaleKeys.token); + log('Token:$token'); emit(state.copyWith(status: RequestStatus.loaded)); }, ); diff --git a/lib/feature/auth/presentation/pages/login_page/login_page.dart b/lib/feature/auth/presentation/pages/login_page/login_page.dart index c05766c..2cb1d9c 100644 --- a/lib/feature/auth/presentation/pages/login_page/login_page.dart +++ b/lib/feature/auth/presentation/pages/login_page/login_page.dart @@ -1,6 +1,5 @@ import 'package:food_delivery_client/feature/auth/presentation/blocs/login_bloc/login_bloc.dart'; import 'package:food_delivery_client/feature/auth/presentation/pages/login_page/widgets/login_body.dart'; - import '../../../../../food_delivery_client.dart'; class LoginPage extends StatelessWidget { @@ -10,12 +9,10 @@ class LoginPage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => sl(), - child: WLayout( - child: Scaffold( - body: WLoginBody(), - ), + child: WLayout( + top: false, + child: Scaffold(resizeToAvoidBottomInset: true, body: WLoginBody()), ), ); } } - diff --git a/lib/feature/auth/presentation/pages/login_page/widgets/login_body.dart b/lib/feature/auth/presentation/pages/login_page/widgets/login_body.dart index e942d87..c317d36 100644 --- a/lib/feature/auth/presentation/pages/login_page/widgets/login_body.dart +++ b/lib/feature/auth/presentation/pages/login_page/widgets/login_body.dart @@ -1,5 +1,7 @@ import 'package:food_delivery_client/core/helpers/formatters.dart'; +import 'package:food_delivery_client/core/helpers/validator_helpers.dart'; import 'package:food_delivery_client/feature/auth/domain/usecases/login_usecase.dart'; +import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_background.dart'; import '../../../../../../food_delivery_client.dart'; import '../../../blocs/login_bloc/login_bloc.dart'; @@ -36,126 +38,130 @@ class _WLoginBodyState extends State { return Form( key: _formKey, autovalidateMode: AutovalidateMode.onUserInteraction, - 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: AppUtils.kBorderRadius8, - controller: _phoneController, - keyBoardType: TextInputType.number, + child: WAuthBackground( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 25.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( + hintText: context.loc.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( - 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().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, + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + Formatters.phoneFormatter, + LengthLimitingTextInputFormatter(12), ], - ).paddingSymmetric(horizontal: 16), - ); - }, + validator: (value) { + return Validators.validatePhoneNumber( + _phoneController.text.trim(), + ); + }, + ), + 10.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, + validator: (value) { + return Validators.validatePassword( + _passwordController.text.trim(), + ); + }, + ), + Align( + alignment: AlignmentGeometry.centerRight, + child: TextButton( + onPressed: () { + context.push(Routes.forgotPassword); + }, + child: Text( + context.loc.forgot_password, + style: AppTextStyles.size14Medium.copyWith( + color: AppColors.c34A853, + ), + ), + ), + ), + 40.verticalSpace, + AppButton( + name: context.loc.continue_str, + isLoading: state.status.isLoading(), + trailing: SvgPicture.asset( + AppIcons.icArrowRightLight, + ).paddingOnly(left: 10), + onPressed: () { + if (_formKey.currentState?.validate() ?? false) { + context.read().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), ), ); }, diff --git a/lib/feature/auth/presentation/pages/register_page/register_page.dart b/lib/feature/auth/presentation/pages/register_page/register_page.dart index 09c5c6f..ccd5875 100644 --- a/lib/feature/auth/presentation/pages/register_page/register_page.dart +++ b/lib/feature/auth/presentation/pages/register_page/register_page.dart @@ -1,3 +1,5 @@ +import 'package:food_delivery_client/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart'; + import '../../../../../food_delivery_client.dart'; class RegisterPage extends StatelessWidget { @@ -6,7 +8,7 @@ class RegisterPage extends StatelessWidget { @override Widget build(BuildContext context) { return WLayout( - child: Scaffold(body: Column(children: [Text("register")])), - ); + top: false, + child: Scaffold(body: WRegisterBody())); } } diff --git a/lib/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart b/lib/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart new file mode 100644 index 0000000..ff606a1 --- /dev/null +++ b/lib/feature/auth/presentation/pages/register_page/widgets/w_register_body.dart @@ -0,0 +1,205 @@ +import 'package:food_delivery_client/core/helpers/validator_helpers.dart'; +import 'package:food_delivery_client/feature/auth/presentation/widgets/w_auth_background.dart'; +import '../../../../../../food_delivery_client.dart'; + +class WRegisterBody extends StatefulWidget { + const WRegisterBody({super.key}); + + @override + State createState() => _WRegisterBodyState(); +} + +class _WRegisterBodyState extends State { + late TextEditingController _firstNameController; + late TextEditingController _lastNameController; + late TextEditingController _phoneNumberController; + late TextEditingController _passwordController; + late TextEditingController _repeatPasswordController; + + final _formKey = GlobalKey(); + + @override + void initState() { + _firstNameController = TextEditingController(); + _lastNameController = TextEditingController(); + _phoneNumberController = TextEditingController(); + _passwordController = TextEditingController(); + _repeatPasswordController = TextEditingController(); + super.initState(); + } + + @override + void dispose() { + _firstNameController.dispose(); + _lastNameController.dispose(); + _phoneNumberController.dispose(); + _passwordController.dispose(); + _repeatPasswordController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Form( + key: _formKey, + child: WAuthBackground( + child: SizedBox( + width: context.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + 20.verticalSpace, + Align( + alignment: AlignmentGeometry.center, + child: Text( + context.loc.sign_up, + style: AppTextStyles.size24Bold, + ), + ), + 20.verticalSpace, + Text( + context.loc.first_name, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + hintText: context.loc.enter_first_name, + controller: _firstNameController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.name, + validator: (value) { + return Validators.validateFields( + _firstNameController.text.trim(), + ); + }, + ), + 10.verticalSpace, + Text( + 'Last name', + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + hintText: "Enter LastName", + controller: _lastNameController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.text, + validator: (value) { + return Validators.validateFields( + _lastNameController.text.trim(), + ); + }, + ), + 10.verticalSpace, + /* Text( + context.loc.phone_number, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + hintText: context.loc.enter_phone_number, + controller: _phoneNumberController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.phone, + prefixIcon: Text("+ 998", style: AppTextStyles.size16Regular), + inputFormatters: [ + FilteringTextInputFormatter.digitsOnly, + Formatters.phoneFormatter, + LengthLimitingTextInputFormatter(12), + ], + validator: (value) { + return Validators.validatePhoneNumber( + _phoneNumberController.text.trim(), + ); + }, + ), + + */ + 10.verticalSpace, + Text( + context.loc.enter_password, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + obscureText: true, + hintText: context.loc.password, + controller: _passwordController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.visiblePassword, + validator: (value) { + return Validators.validatePassword( + _passwordController.text.trim(), + ); + }, + ), + 10.verticalSpace, + + Text( + context.loc.repeat_password, + style: AppTextStyles.size14Regular.copyWith( + color: AppColors.c6A6E7F, + ), + ), + 5.verticalSpace, + AppTextFormField( + obscureText: true, + hintText: context.loc.enter_repeat_password, + controller: _repeatPasswordController, + borderRadius: AppUtils.kBorderRadius8, + keyBoardType: TextInputType.visiblePassword, + validator: (value) { + return Validators.validatePassword( + _repeatPasswordController.text.trim(), + ); + }, + ), + 20.verticalSpace, + AppButton( + name: context.loc.continue_str, + borderRadius: 15, + + backgroundColor: AppColors.c34A853, + trailing: SvgPicture.asset( + AppIcons.icArrowRightLight, + ).paddingOnly(left: 8), + onPressed: () { + if (_formKey.currentState?.validate() ?? false) {} + }, + ), + 20.verticalSpace, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.loc.already_has_account, + style: AppTextStyles.size14Medium, + ), + TextButton( + onPressed: () { + context.pushReplacement(Routes.login); + }, + child: Text( + context.loc.login, + style: AppTextStyles.size15Bold.copyWith( + color: AppColors.c34A853, + ), + ), + ), + ], + ), + ], + ).paddingSymmetric(horizontal: 16), + ), + ), + ); + } +} diff --git a/lib/feature/auth/presentation/widgets/w_auth_background.dart b/lib/feature/auth/presentation/widgets/w_auth_background.dart new file mode 100644 index 0000000..c34e1d4 --- /dev/null +++ b/lib/feature/auth/presentation/widgets/w_auth_background.dart @@ -0,0 +1,35 @@ +import '../../../../food_delivery_client.dart'; + +class WAuthBackground extends StatelessWidget { + const WAuthBackground({super.key, required this.child}); + + final Widget child; + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + SizedBox( + height: context.h * .3, + child: Image.asset(AppImages.imgBurger2, fit: BoxFit.cover), + ), + SingleChildScrollView( + // keyboardDismissBehavior: + // ScrollViewKeyboardDismissBehavior.onDrag, + child: Column( + children: [ + SizedBox(height: context.h * .2), + DecoratedBox( + decoration: BoxDecoration( + color: AppColors.cFFFFFF, + borderRadius: AppUtils.kBorderRadiusTop20, + ), + child: child, + ), + ], + ), + ), + ], + ); + } +} diff --git a/lib/feature/common/presentation/widgets/app_text_form_field.dart b/lib/feature/common/presentation/widgets/app_text_form_field.dart index b2a9dca..e30faf8 100644 --- a/lib/feature/common/presentation/widgets/app_text_form_field.dart +++ b/lib/feature/common/presentation/widgets/app_text_form_field.dart @@ -1,7 +1,7 @@ import '../../../../food_delivery_client.dart'; class AppTextFormField extends StatefulWidget { - const AppTextFormField({ + AppTextFormField({ super.key, this.maxLines, this.minLines, @@ -22,6 +22,7 @@ class AppTextFormField extends StatefulWidget { this.borderRadius, this.height, this.suffixIcon, + this.validator, }); final int? maxLines; @@ -43,78 +44,78 @@ class AppTextFormField extends StatefulWidget { final BorderRadius? borderRadius; final double? height; final Widget? suffixIcon; + FormFieldValidator? validator; @override State createState() => _AppTextFormFieldState(); } class _AppTextFormFieldState extends State { - FormFieldValidator? validator; - bool visibility = true; @override Widget build(BuildContext context) { - return SizedBox( - height: widget.height ?? 44, - child: TextFormField( + return TextFormField( + enabled: true, + //autofocus: true, + 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: widget.validator, + obscureText: widget.obscureText ? visibility : false, + obscuringCharacter: "*", + autovalidateMode: AutovalidateMode.onUserInteraction, + decoration: InputDecoration( enabled: true, - autofocus: true, - 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: 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: widget.prefixIcon?.paddingOnly(left: 10).paddingAll(3), - contentPadding: EdgeInsets.symmetric(vertical: 12, horizontal: 12), - border: OutlineInputBorder( - borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, - borderSide: BorderSide.none, - ), - errorBorder: OutlineInputBorder( - borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, - borderSide: BorderSide.none, - ), - enabledBorder: OutlineInputBorder( - borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, - borderSide: BorderSide.none, - ), - focusedBorder: OutlineInputBorder( - borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, - borderSide: BorderSide.none, - ), - disabledBorder: OutlineInputBorder( - borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, - borderSide: BorderSide.none, - ), + filled: true, + + fillColor: widget.fillColor ?? AppColors.cEEEEEE, + hintText: widget.hintText, + hintStyle: + widget.hintTextStyle ?? + AppTextStyles.size16Regular.copyWith(color: AppColors.c7F7F7F), + suffixIcon: widget.obscureText + ? IconButton( + onPressed: () { + setState(() { + visibility = !visibility; + }); + }, + icon: Icon( + visibility ? Icons.visibility : Icons.visibility_off, + color: AppColors.c000000, + ), + ) + : widget.suffixIcon, + prefixIconConstraints: BoxConstraints(minHeight: 24, minWidth: 0), + prefixIcon: widget.prefixIcon?.paddingOnly(left: 10).paddingAll(3), + contentPadding: EdgeInsets.symmetric(vertical: 12, horizontal: 12), + border: OutlineInputBorder( + borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, + borderSide: BorderSide.none, + ), + errorBorder: OutlineInputBorder( + borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, + borderSide: BorderSide.none, + ), + disabledBorder: OutlineInputBorder( + borderRadius: widget.borderRadius ?? AppUtils.kBorderRadius40, + borderSide: BorderSide.none, ), ), ); diff --git a/lib/feature/common/presentation/widgets/w_toastification.dart b/lib/feature/common/presentation/widgets/w_toastification.dart index c87d1a6..9440fac 100644 --- a/lib/feature/common/presentation/widgets/w_toastification.dart +++ b/lib/feature/common/presentation/widgets/w_toastification.dart @@ -6,7 +6,10 @@ showErrorToast(String? message) { context: navigatorKey.currentContext, type: ToastificationType.error, style: ToastificationStyle.fillColored, - title: Text(message ?? "", maxLines: 5), + title: Text( + message ?? navigatorKey.currentContext!.loc.unexpected_error, + maxLines: 5, + ), autoCloseDuration: TimeDelayConst.duration3, ); }