diff --git a/assets/icons/ic_arrow_right_light.svg b/assets/icons/ic_arrow_right_light.svg new file mode 100644 index 0000000..fefd449 --- /dev/null +++ b/assets/icons/ic_arrow_right_light.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/translations/app_en.arb b/assets/translations/app_en.arb index 3b21ecb..e9065e7 100644 --- a/assets/translations/app_en.arb +++ b/assets/translations/app_en.arb @@ -156,7 +156,16 @@ "baskets": "Baskets", "account": "Account", "changeLanguage": "Change language", - "language": "Language" + "language": "Language", + "login": "Login", + "phone_number": "Phone number", + "enter_phone_number": "Enter phone number", + "password": "Password", + "enter_password": "Enter password", + "forgot_password": "Forgot password", + "Continue": "Continue", + "dont_have_account": "Don't have an account?", + "sign_up": "SignUp" diff --git a/assets/translations/app_ru.arb b/assets/translations/app_ru.arb index f990c18..a8c8f51 100644 --- a/assets/translations/app_ru.arb +++ b/assets/translations/app_ru.arb @@ -151,7 +151,16 @@ "baskets": "Корзина", "account": "Аккаунт", "language": "Язык", - "changeLanguage": "Сменить язык" + "changeLanguage": "Сменить язык", + "login": "Вход", + "phone_number": "Номер телефона", + "enter_phone_number": "Введите номер телефона", + "password": "Пароль", + "enter_password": "Введите пароль", + "forgot_password": "Забыли пароль", + "Continue": "Продолжить", + "dont_have_account": "У вас нет аккаунта?", + "sign_up": "Зарегистрироваться" diff --git a/assets/translations/app_uz.arb b/assets/translations/app_uz.arb index edae951..4d32f82 100644 --- a/assets/translations/app_uz.arb +++ b/assets/translations/app_uz.arb @@ -151,7 +151,16 @@ "baskets": "Savatcha", "account": "Profil", "language": "Til", - "changeLanguage": "Tilni o'zgartirish" + "changeLanguage": "Tilni o'zgartirish", + "login": "Kirish", + "phone_number": "Telefon raqami", + "enter_phone_number": "Telefon raqamingizni kiriting", + "password": "Parol", + "enter_password": "Parolni kiriting", + "forgot_password": "Parolni unutdingizmi", + "Continue": "Davom etish", + "dont_have_account": "Hisobingiz yo‘qmi?", + "sign_up": "Ro‘yxatdan o‘tish" diff --git a/lib/core/constants/app_locale_keys.dart b/lib/core/constants/app_locale_keys.dart index c5ed397..9580854 100644 --- a/lib/core/constants/app_locale_keys.dart +++ b/lib/core/constants/app_locale_keys.dart @@ -2,6 +2,7 @@ abstract class AppLocaleKeys { ///Storage keys static const String language = 'language'; static const String browseSearchHistory = 'browse-search-history'; + static const String token = 'token'; static const String fontBold = "fontBold"; static const String fontMedium = "fontMedium"; diff --git a/lib/core/di/injection_container.config.dart b/lib/core/di/injection_container.config.dart index a95edaf..78c31b5 100644 --- a/lib/core/di/injection_container.config.dart +++ b/lib/core/di/injection_container.config.dart @@ -71,7 +71,10 @@ extension GetItInjectableX on _i174.GetIt { () => _i241.LoginUseCase(gh<_i884.AuthRepository>()), ); gh.factory<_i1065.LoginBloc>( - () => _i1065.LoginBloc(gh<_i241.LoginUseCase>()), + () => _i1065.LoginBloc( + gh<_i241.LoginUseCase>(), + gh<_i321.StorageService>(), + ), ); return this; } diff --git a/lib/core/l10n/app_localizations.dart b/lib/core/l10n/app_localizations.dart index da15842..9f8ce3a 100644 --- a/lib/core/l10n/app_localizations.dart +++ b/lib/core/l10n/app_localizations.dart @@ -63,8 +63,7 @@ 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; @@ -72,8 +71,7 @@ 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. @@ -85,19 +83,18 @@ 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. @@ -753,10 +750,63 @@ abstract class AppLocalizations { /// In en, this message translates to: /// **'Language'** String get language; + + /// No description provided for @login. + /// + /// In en, this message translates to: + /// **'Login'** + String get login; + + /// No description provided for @phone_number. + /// + /// In en, this message translates to: + /// **'Phone number'** + String get phone_number; + + /// No description provided for @enter_phone_number. + /// + /// In en, this message translates to: + /// **'Enter phone number'** + String get enter_phone_number; + + /// No description provided for @password. + /// + /// In en, this message translates to: + /// **'Password'** + String get password; + + /// No description provided for @enter_password. + /// + /// In en, this message translates to: + /// **'Enter password'** + String get enter_password; + + /// No description provided for @forgot_password. + /// + /// In en, this message translates to: + /// **'Forgot password'** + String get forgot_password; + + /// No description provided for @continue. + /// + /// In en, this message translates to: + /// **'Continue'** + String get Continue; + + /// No description provided for @dont_have_account. + /// + /// In en, this message translates to: + /// **'Don\'t have an account?'** + String get dont_have_account; + + /// No description provided for @sign_up. + /// + /// In en, this message translates to: + /// **'SignUp'** + String get sign_up; } -class _AppLocalizationsDelegate - extends LocalizationsDelegate { +class _AppLocalizationsDelegate extends LocalizationsDelegate { const _AppLocalizationsDelegate(); @override @@ -765,28 +815,26 @@ class _AppLocalizationsDelegate } @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 1d91b16..708e9a3 100644 --- a/lib/core/l10n/app_localizations_en.dart +++ b/lib/core/l10n/app_localizations_en.dart @@ -21,8 +21,7 @@ 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'; @@ -239,8 +238,7 @@ 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'; @@ -346,4 +344,31 @@ class AppLocalizationsEn extends AppLocalizations { @override String get language => 'Language'; + + @override + String get login => 'Login'; + + @override + String get phone_number => 'Phone number'; + + @override + String get enter_phone_number => 'Enter phone number'; + + @override + String get password => 'Password'; + + @override + String get enter_password => 'Enter password'; + + @override + String get forgot_password => 'Forgot password'; + + @override + String get Continue => 'Continue'; + + @override + String get dont_have_account => 'Don\'t have an account?'; + + @override + String get sign_up => 'SignUp'; } diff --git a/lib/core/l10n/app_localizations_ru.dart b/lib/core/l10n/app_localizations_ru.dart index ded80b9..a43e030 100644 --- a/lib/core/l10n/app_localizations_ru.dart +++ b/lib/core/l10n/app_localizations_ru.dart @@ -9,8 +9,7 @@ class AppLocalizationsRu extends AppLocalizations { AppLocalizationsRu([String locale = 'ru']) : super(locale); @override - String get useYourTAxiAccount => - 'Используйте свой аккаунт Uber, чтобы начать'; + String get useYourTAxiAccount => 'Используйте свой аккаунт Uber, чтобы начать'; @override String get enterYourMobileNumber => 'Введите свой номер телефона'; @@ -22,8 +21,7 @@ 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'; @@ -240,8 +238,7 @@ class AppLocalizationsRu extends AppLocalizations { String get addItemsStartBasket => 'Добавьте товары, чтобы создать корзину'; @override - String get basketHint => - 'Когда вы добавите товары из ресторана или магазина, ваша корзина появится здесь.'; + String get basketHint => 'Когда вы добавите товары из ресторана или магазина, ваша корзина появится здесь.'; @override String get startShopping => 'Начать покупки'; @@ -347,4 +344,31 @@ class AppLocalizationsRu extends AppLocalizations { @override String get language => 'Язык'; + + @override + String get login => 'Login'; + + @override + String get phone_number => 'Phone number'; + + @override + String get enter_phone_number => 'Enter phone number'; + + @override + String get password => 'Password'; + + @override + String get enter_password => 'Enter password'; + + @override + String get forgot_password => 'Forgot password'; + + @override + String get Continue => 'Continue'; + + @override + String get dont_have_account => 'Don\'t have an account?'; + + @override + String get sign_up => 'SignUp'; } diff --git a/lib/core/l10n/app_localizations_uz.dart b/lib/core/l10n/app_localizations_uz.dart index 52cc231..ad38260 100644 --- a/lib/core/l10n/app_localizations_uz.dart +++ b/lib/core/l10n/app_localizations_uz.dart @@ -9,8 +9,7 @@ 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'; @@ -22,8 +21,7 @@ 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'; @@ -240,8 +238,7 @@ 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'; @@ -347,4 +344,31 @@ class AppLocalizationsUz extends AppLocalizations { @override String get language => 'Til'; + + @override + String get login => 'Login'; + + @override + String get phone_number => 'Phone number'; + + @override + String get enter_phone_number => 'Enter phone number'; + + @override + String get password => 'Password'; + + @override + String get enter_password => 'Enter password'; + + @override + String get forgot_password => 'Forgot password'; + + @override + String get Continue => 'Continue'; + + @override + String get dont_have_account => 'Don\'t have an account?'; + + @override + String get sign_up => 'SignUp'; } diff --git a/lib/core/network/dio_client.dart b/lib/core/network/dio_client.dart index f2adb50..6052a91 100644 --- a/lib/core/network/dio_client.dart +++ b/lib/core/network/dio_client.dart @@ -1,5 +1,4 @@ import 'package:dio/dio.dart'; -import 'package:flutter/foundation.dart'; import 'package:food_delivery_client/core/network/header_interceptors.dart'; import 'package:food_delivery_client/food_delivery_client.dart'; import 'error_handler_interceptor.dart'; diff --git a/lib/core/router/app_routes.dart b/lib/core/router/app_routes.dart index 14a2e7c..36f59e7 100644 --- a/lib/core/router/app_routes.dart +++ b/lib/core/router/app_routes.dart @@ -1,5 +1,7 @@ import 'package:flutter/cupertino.dart'; +import 'package:food_delivery_client/feature/auth/presentation/pages/forgot_password_page/forgot_password_page.dart'; import 'package:food_delivery_client/feature/auth/presentation/pages/login_page/login_page.dart'; +import 'package:food_delivery_client/feature/auth/presentation/pages/register_page/register_page.dart'; import 'package:food_delivery_client/feature/home/presentation/pages/restaurants_by_category_page/restaurants_by_category_page.dart'; import '../../food_delivery_client.dart'; @@ -18,6 +20,16 @@ class AppRoutes { pageBuilder: (context, state) => CupertinoPage(child: LoginPage()), ), + GoRoute( + path: Routes.register, + pageBuilder: (context, state) => CupertinoPage(child: RegisterPage()), + ), + GoRoute( + path: Routes.forgotPassword, + pageBuilder: (context, state) => + CupertinoPage(child: ForgotPasswordPage()), + ), + GoRoute( path: Routes.main, pageBuilder: (context, state) => CupertinoPage(child: MainPage()), diff --git a/lib/core/router/routes_name.dart b/lib/core/router/routes_name.dart index b34a8d7..bf001c0 100644 --- a/lib/core/router/routes_name.dart +++ b/lib/core/router/routes_name.dart @@ -5,6 +5,7 @@ abstract class Routes { static const String main = '/main'; static const String categories = '/categories'; static const String filters = '/filters'; - static const String browse = '/browse'; + static const String browse = '/browse'; + static const String forgotPassword = "/forgot-password"; static const String restaurantsByCategory = '/restaurants-by-category'; } diff --git a/lib/core/theme/app_icons.dart b/lib/core/theme/app_icons.dart index b8d6568..f39b273 100644 --- a/lib/core/theme/app_icons.dart +++ b/lib/core/theme/app_icons.dart @@ -48,6 +48,7 @@ abstract class AppIcons { static const String icWallet = "$baseUrl/ic_wallet.svg"; static const String icOrdersSvg = "$baseUrl/ic_orders_svg.svg"; static const String icLanguage = "$baseUrl/ic_language.svg"; + static const String icArrowRightLight = "$baseUrl/ic_arrow_right_light.svg"; ///.png icons static const String icBestOverall = "$baseUrl/ic_best.png"; diff --git a/lib/core/theme/app_images.dart b/lib/core/theme/app_images.dart index 3350b33..aac541f 100644 --- a/lib/core/theme/app_images.dart +++ b/lib/core/theme/app_images.dart @@ -23,4 +23,5 @@ abstract class AppImages { static const String imgDesert = "$baseUrl/img_desert.png"; static const String imgPickUp = "$baseUrl/img_pick_up.png"; static const String imgAvatar = "$baseUrl/img_avatar.jpg"; + static const String imgBurger2 = "$baseUrl/img_burger2.png"; } diff --git a/lib/core/theme/app_textstyles.dart b/lib/core/theme/app_textstyles.dart index a2161b9..e928d03 100644 --- a/lib/core/theme/app_textstyles.dart +++ b/lib/core/theme/app_textstyles.dart @@ -83,6 +83,19 @@ abstract class AppTextStyles { fontFamily: _fontMedium, fontWeight: FontWeight.w500, ); + static const TextStyle size14Bold= TextStyle( + color: _defaultColor, + fontSize: SizesCons.size_14, + fontFamily: _fontBold, + fontWeight: FontWeight.w700, + ); + + static const TextStyle size15Bold= TextStyle( + color: _defaultColor, + fontSize: SizesCons.size_15, + fontFamily: _fontBold, + fontWeight: FontWeight.w700, + ); static const TextStyle size16Bold= TextStyle( color: _defaultColor, 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 492f067..1c2e3b2 100644 --- a/lib/feature/auth/presentation/blocs/login_bloc/login_bloc.dart +++ b/lib/feature/auth/presentation/blocs/login_bloc/login_bloc.dart @@ -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 { 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 { 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)); }, ); diff --git a/lib/feature/auth/presentation/pages/forgot_password_page/forgot_password_page.dart b/lib/feature/auth/presentation/pages/forgot_password_page/forgot_password_page.dart new file mode 100644 index 0000000..25770b8 --- /dev/null +++ b/lib/feature/auth/presentation/pages/forgot_password_page/forgot_password_page.dart @@ -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')])), + ); + } +} 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 06ac749..c05766c 100644 --- a/lib/feature/auth/presentation/pages/login_page/login_page.dart +++ b/lib/feature/auth/presentation/pages/login_page/login_page.dart @@ -10,7 +10,7 @@ class LoginPage extends StatelessWidget { Widget build(BuildContext context) { return BlocProvider( create: (context) => sl(), - child: WLayout( + child: WLayout( child: Scaffold( 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 69bec77..e942d87 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,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 { 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().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().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 new file mode 100644 index 0000000..09c5c6f --- /dev/null +++ b/lib/feature/auth/presentation/pages/register_page/register_page.dart @@ -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")])), + ); + } +} diff --git a/lib/feature/common/presentation/widgets/app_button.dart b/lib/feature/common/presentation/widgets/app_button.dart index e343581..7863f49 100644 --- a/lib/feature/common/presentation/widgets/app_button.dart +++ b/lib/feature/common/presentation/widgets/app_button.dart @@ -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( 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 7aa781a..b2a9dca 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 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? inputFormatters; final TextInputType? keyBoardType; - FormFieldValidator? 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 createState() => _AppTextFormFieldState(); +} + +class _AppTextFormFieldState extends State { + FormFieldValidator? 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, ), ), diff --git a/lib/feature/common/presentation/widgets/w_toastification.dart b/lib/feature/common/presentation/widgets/w_toastification.dart new file mode 100644 index 0000000..c87d1a6 --- /dev/null +++ b/lib/feature/common/presentation/widgets/w_toastification.dart @@ -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, + ); +} diff --git a/lib/main.dart b/lib/main.dart index 020c6bf..0aaf1b7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:food_delivery_client/feature/common/presentation/blocs/language_bloc/language_bloc.dart'; +import 'package:toastification/toastification.dart'; import 'food_delivery_client.dart'; @@ -44,28 +45,30 @@ class _MyAppState extends State { return BlocBuilder( bloc: context.read()..add(LanguageEvent.started()), builder: (context, state) { - return MaterialApp.router( - title: "Uber Eats", - debugShowCheckedModeBanner: false, - theme: AppTheme.lightTheme, - routerConfig: sl().router, - locale: state.currentLocale, - supportedLocales: L10n.locales, - localizationsDelegates: [ - AppLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - builder: (context, child) => GestureDetector( - onTap: () { - FocusManager.instance.primaryFocus?.unfocus(); - }, - child: MediaQuery( - data: MediaQuery.of( - context, - ).copyWith(textScaler: const TextScaler.linear(1)), - child: child!, + return ToastificationWrapper( + child: MaterialApp.router( + title: "Uber Eats", + debugShowCheckedModeBanner: false, + theme: AppTheme.lightTheme, + routerConfig: sl().router, + locale: state.currentLocale, + supportedLocales: L10n.locales, + localizationsDelegates: [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + builder: (context, child) => GestureDetector( + onTap: () { + FocusManager.instance.primaryFocus?.unfocus(); + }, + child: MediaQuery( + data: MediaQuery.of( + context, + ).copyWith(textScaler: const TextScaler.linear(1)), + child: child!, + ), ), ), ); diff --git a/pubspec.lock b/pubspec.lock index 15da394..8319a88 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -397,6 +397,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + iconsax_flutter: + dependency: transitive + description: + name: iconsax_flutter + sha256: d14b4cec8586025ac15276bdd40f6eea308cb85748135965bb6255f14beb2564 + url: "https://pub.dev" + source: hosted + version: "1.0.1" injectable: dependency: "direct main" description: @@ -605,6 +613,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + pausable_timer: + dependency: transitive + description: + name: pausable_timer + sha256: "6ef1a95441ec3439de6fb63f39a011b67e693198e7dae14e20675c3c00e86074" + url: "https://pub.dev" + source: hosted + version: "3.1.0+3" petitparser: dependency: transitive description: @@ -882,6 +898,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.6" + toastification: + dependency: "direct main" + description: + name: toastification + sha256: "69db2bff425b484007409650d8bcd5ed1ce2e9666293ece74dcd917dacf23112" + url: "https://pub.dev" + source: hosted + version: "3.0.3" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b24a690..aae1427 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,7 +23,9 @@ dependencies: equatable: ^2.0.7 dartz: ^0.10.1 mask_text_input_formatter: ^2.9.0 - + + #for notify user about error or success + toastification: ^3.0.3 #DI get_it: ^8.2.0