Compare commits

...

4 Commits

Author SHA1 Message Date
jahongireshonqulov
8c4e082c57 feat:resetting password done 2025-11-01 20:41:28 +05:00
jahongireshonqulov
c5a6c2dbed feat:verify your account page ui done 2025-11-01 18:33:03 +05:00
jahongireshonqulov
0b562d1dcc feat:theme changing done 2025-11-01 15:37:09 +05:00
jahongireshonqulov
b734fc9ce6 feat:theme changing done 2025-11-01 15:37:05 +05:00
35 changed files with 1657 additions and 78 deletions

View File

@@ -1,6 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application <application
android:allowBackup="false"
android:fullBackupContent="false"
android:label="food_delivery_client" android:label="food_delivery_client"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">

View File

@@ -0,0 +1,3 @@
<svg width="49" height="56" viewBox="0 0 49 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M39.3392 31.4713C40.2309 31.4713 41.0963 31.5762 41.9618 31.7074V23.6035C41.9618 22.2124 41.4092 20.8782 40.4255 19.8946C39.4418 18.9109 38.1077 18.3583 36.7166 18.3583H34.0939V13.1131C34.0939 5.87465 28.2193 0 20.9809 0C13.7425 0 7.86783 5.87465 7.86783 13.1131V18.3583H5.24522C3.8541 18.3583 2.51996 18.9109 1.53629 19.8946C0.55262 20.8782 0 22.2124 0 23.6035V49.8296C0 52.7407 2.33412 55.0748 5.24522 55.0748H25.7278C24.3903 52.7669 23.6035 50.0919 23.6035 47.207C23.6035 38.5262 30.6583 31.4713 39.3392 31.4713ZM13.1131 13.1131C13.1131 8.75952 16.6274 5.24522 20.9809 5.24522C25.3344 5.24522 28.8487 8.75952 28.8487 13.1131V18.3583H13.1131V13.1131ZM20.9809 41.9618C19.9435 41.9618 18.9294 41.6542 18.0668 41.0778C17.2042 40.5015 16.5319 39.6823 16.1349 38.7238C15.7379 37.7654 15.6341 36.7107 15.8365 35.6933C16.0388 34.6758 16.5384 33.7412 17.272 33.0076C18.0055 32.2741 18.9401 31.7745 19.9576 31.5721C20.9751 31.3697 22.0297 31.4736 22.9881 31.8706C23.9466 32.2676 24.7658 32.9399 25.3421 33.8025C25.9185 34.665 26.2261 35.6792 26.2261 36.7166C26.2261 39.6277 23.892 41.9618 20.9809 41.9618ZM48.5183 42.6174L36.0609 55.0748L28.8487 47.207L31.891 44.1648L36.0609 48.3347L45.4761 38.9196L48.5183 42.6174Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -21,7 +21,7 @@
}, },
"please_login": "Please login to your account", "please_login": "Please login to your account",
"email_or_phone": "Phone Number", "email_or_phone": "Phone Number",
"enter_email_or_phone": "Enter your email or phone number", "enter_email_or_phone": "Enter your phone number",
"password": "Password", "password": "Password",
"enter_password": "Enter your password", "enter_password": "Enter your password",
"forgot_password": "Forgot Password?", "forgot_password": "Forgot Password?",
@@ -35,6 +35,25 @@
"discover": "Discover", "discover": "Discover",
"cart": "Cart", "cart": "Cart",
"transactions": "Transactions", "transactions": "Transactions",
"profile": "Profile" "profile": "Profile",
"forgotPassword": "Forgot Password",
"forgotPasswordSubtitle": "Please enter your phone number to reset your password.",
"phoneNumber": "Phone Number",
"sendCode": "Send Code",
"verifyYourAccount": "Verify Your Account",
"verifyAccountSubtitle": "Weve sent a verification code to",
"verifyAccount": "Verify Account",
"didNotReceiveCode": "Didnt receive the code?",
"resend": "Resend",
"createNewPassword": "Create New Password",
"createNewPasswordSubtitle": "Please create your new password.",
"oldPassword": "Old Password",
"newPassword": "New Password",
"confirmNewPassword": "Confirm New Password",
"passwordHint": "*Password must have at least 8 characters, including number and symbol.",
"resetPassword": "Reset Password",
"changePasswordSuccess": "Change Password Success",
"changePasswordMessage": "We have updated your password. Please remember your password, thank you!",
"login": "Login"
} }

View File

@@ -21,7 +21,7 @@
}, },
"please_login": "Пожалуйста, войдите в свой аккаунт", "please_login": "Пожалуйста, войдите в свой аккаунт",
"email_or_phone": "Номер телефона", "email_or_phone": "Номер телефона",
"enter_email_or_phone": "Введите свою почту или номер телефона", "enter_email_or_phone": "Введите свою номер телефона",
"password": "Пароль", "password": "Пароль",
"enter_password": "Введите свой пароль", "enter_password": "Введите свой пароль",
"forgot_password": "Забыли пароль?", "forgot_password": "Забыли пароль?",
@@ -35,6 +35,25 @@
"discover": "Обзор", "discover": "Обзор",
"cart": "Корзина", "cart": "Корзина",
"transactions": "Транзакции", "transactions": "Транзакции",
"profile": "Профиль" "profile": "Профиль",
"forgotPassword": "Забыли пароль",
"forgotPasswordSubtitle": "Пожалуйста, введите свой номер телефона, чтобы сбросить пароль.",
"phoneNumber": "Номер телефона",
"sendCode": "Отправить код",
"verifyYourAccount": "Подтвердите аккаунт",
"verifyAccountSubtitle": "Мы отправили код подтверждения на",
"verifyAccount": "Подтвердить",
"didNotReceiveCode": "Не получили код?",
"resend": "Отправить повторно",
"createNewPassword": "Создайте новый пароль",
"createNewPasswordSubtitle": "Пожалуйста, создайте новый пароль.",
"oldPassword": "Старый пароль",
"newPassword": "Новый пароль",
"confirmNewPassword": "Подтвердите пароль",
"passwordHint": "*Пароль должен содержать не менее 8 символов, включая цифры и символы.",
"resetPassword": "Сбросить пароль",
"changePasswordSuccess": "Пароль успешно изменён",
"changePasswordMessage": "Ваш пароль был обновлён. Пожалуйста, запомните его. Спасибо!",
"login": "Войти"
} }

View File

@@ -21,7 +21,7 @@
}, },
"please_login": "Iltimos, akkauntingizga kiring", "please_login": "Iltimos, akkauntingizga kiring",
"email_or_phone": "Telefon raqami", "email_or_phone": "Telefon raqami",
"enter_email_or_phone": "Email yoki telefon raqamingizni kiriting", "enter_email_or_phone": "Telefon raqamingizni kiriting",
"password": "Parol", "password": "Parol",
"enter_password": "Parolingizni kiriting", "enter_password": "Parolingizni kiriting",
"forgot_password": "Parolni unutdingizmi?", "forgot_password": "Parolni unutdingizmi?",
@@ -35,6 +35,25 @@
"discover": "Kashf etish", "discover": "Kashf etish",
"cart": "Savat", "cart": "Savat",
"transactions": "Tranzaksiyalar", "transactions": "Tranzaksiyalar",
"profile": "Profil" "profile": "Profil",
"forgotPassword": "Parolni unutdingizmi",
"forgotPasswordSubtitle": "Parolingizni tiklash uchun telefon raqamingizni kiriting.",
"phoneNumber": "Telefon raqami",
"sendCode": "Kod yuborish",
"verifyYourAccount": "Hisobni tasdiqlang",
"verifyAccountSubtitle": "Tasdiqlash kodi quyidagi raqamga yuborildi",
"verifyAccount": "Hisobni tasdiqlash",
"didNotReceiveCode": "Kod kelmadimi?",
"resend": "Qayta yuborish",
"createNewPassword": "Yangi parol yarating",
"createNewPasswordSubtitle": "Iltimos, yangi parolingizni yarating.",
"oldPassword": "Eski parol",
"newPassword": "Yangi parol",
"confirmNewPassword": "Parolni tasdiqlang",
"passwordHint": "*Parol kamida 8 ta belgi, raqam va belgidan iborat bolishi kerak.",
"resetPassword": "Parolni tiklash",
"changePasswordSuccess": "Parol muvaffaqiyatli ozgartirildi",
"changePasswordMessage": "Parolingiz yangilandi. Iltimos, uni eslab qoling. Rahmat!",
"login": "Kirish"
} }

View File

@@ -3,6 +3,8 @@ abstract class AppLocaleKeys {
static const String language = 'language'; static const String language = 'language';
static const String browseSearchHistory = 'browse-search-history'; static const String browseSearchHistory = 'browse-search-history';
static const String token = 'token'; static const String token = 'token';
static const String theme = 'theme';
static const String appName = "Felix Eats"; static const String appName = "Felix Eats";

View File

@@ -40,6 +40,8 @@ import '../../feature/auth/presentation/blocs/verify_phone_bloc/verify_phone_blo
as _i224; as _i224;
import '../../feature/common/presentation/blocs/language_bloc/language_bloc.dart' import '../../feature/common/presentation/blocs/language_bloc/language_bloc.dart'
as _i942; as _i942;
import '../../feature/common/presentation/blocs/theme_bloc/theme_bloc.dart'
as _i465;
import '../../feature/main/presentation/blocs/main_bloc/main_bloc.dart' import '../../feature/main/presentation/blocs/main_bloc/main_bloc.dart'
as _i580; as _i580;
import '../../feature/onboarding/presentation/blocs/splash_bloc/splash_bloc.dart' import '../../feature/onboarding/presentation/blocs/splash_bloc/splash_bloc.dart'
@@ -68,6 +70,9 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i28.SplashBloc>( gh.factory<_i28.SplashBloc>(
() => _i28.SplashBloc(gh<_i321.StorageService>()), () => _i28.SplashBloc(gh<_i321.StorageService>()),
); );
gh.factory<_i465.ThemeBloc>(
() => _i465.ThemeBloc(gh<_i321.StorageService>()),
);
gh.lazySingleton<_i361.Dio>(() => dioModule.dio(gh<_i667.DioClient>())); gh.lazySingleton<_i361.Dio>(() => dioModule.dio(gh<_i667.DioClient>()));
gh.singleton<_i354.RequestHandlerService>( gh.singleton<_i354.RequestHandlerService>(
() => _i354.RequestHandlerService(gh<_i361.Dio>()), () => _i354.RequestHandlerService(gh<_i361.Dio>()),

View File

@@ -0,0 +1,22 @@
class StringHelpers {
static String formatUzbekPhoneNumber(String input) {
final digits = input.replaceAll(RegExp(r'\D'), '');
String formatted = digits;
if (formatted.startsWith('998')) {
formatted = formatted.substring(3);
} else if (formatted.startsWith('8')) {
formatted = formatted.substring(1);
}
final buffer = StringBuffer('+998 ');
for (int i = 0; i < formatted.length && i < 9; i++) {
buffer.write(formatted[i]);
if (i == 1 || i == 4 || i == 6) {
buffer.write(' ');
}
}
return buffer.toString().trim();
}
}

View File

@@ -193,7 +193,7 @@ abstract class AppLocalizations {
/// No description provided for @enter_email_or_phone. /// No description provided for @enter_email_or_phone.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Enter your email or phone number'** /// **'Enter your phone number'**
String get enter_email_or_phone; String get enter_email_or_phone;
/// No description provided for @password. /// No description provided for @password.
@@ -279,6 +279,114 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'Profile'** /// **'Profile'**
String get profile; String get profile;
/// No description provided for @forgotPassword.
///
/// In en, this message translates to:
/// **'Forgot Password'**
String get forgotPassword;
/// No description provided for @forgotPasswordSubtitle.
///
/// In en, this message translates to:
/// **'Please enter your phone number to reset your password.'**
String get forgotPasswordSubtitle;
/// No description provided for @phoneNumber.
///
/// In en, this message translates to:
/// **'Phone Number'**
String get phoneNumber;
/// No description provided for @sendCode.
///
/// In en, this message translates to:
/// **'Send Code'**
String get sendCode;
/// No description provided for @verifyYourAccount.
///
/// In en, this message translates to:
/// **'Verify Your Account'**
String get verifyYourAccount;
/// No description provided for @verifyAccountSubtitle.
///
/// In en, this message translates to:
/// **'Weve sent a verification code to'**
String get verifyAccountSubtitle;
/// No description provided for @verifyAccount.
///
/// In en, this message translates to:
/// **'Verify Account'**
String get verifyAccount;
/// No description provided for @didNotReceiveCode.
///
/// In en, this message translates to:
/// **'Didnt receive the code?'**
String get didNotReceiveCode;
/// No description provided for @resend.
///
/// In en, this message translates to:
/// **'Resend'**
String get resend;
/// No description provided for @createNewPassword.
///
/// In en, this message translates to:
/// **'Create New Password'**
String get createNewPassword;
/// No description provided for @createNewPasswordSubtitle.
///
/// In en, this message translates to:
/// **'Please create your new password.'**
String get createNewPasswordSubtitle;
/// No description provided for @oldPassword.
///
/// In en, this message translates to:
/// **'Old Password'**
String get oldPassword;
/// No description provided for @newPassword.
///
/// In en, this message translates to:
/// **'New Password'**
String get newPassword;
/// No description provided for @confirmNewPassword.
///
/// In en, this message translates to:
/// **'Confirm New Password'**
String get confirmNewPassword;
/// No description provided for @passwordHint.
///
/// In en, this message translates to:
/// **'*Password must have at least 8 characters, including number and symbol.'**
String get passwordHint;
/// No description provided for @resetPassword.
///
/// In en, this message translates to:
/// **'Reset Password'**
String get resetPassword;
/// No description provided for @changePasswordSuccess.
///
/// In en, this message translates to:
/// **'Change Password Success'**
String get changePasswordSuccess;
/// No description provided for @changePasswordMessage.
///
/// In en, this message translates to:
/// **'We have updated your password. Please remember your password, thank you!'**
String get changePasswordMessage;
} }
class _AppLocalizationsDelegate class _AppLocalizationsDelegate

View File

@@ -62,7 +62,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get email_or_phone => 'Phone Number'; String get email_or_phone => 'Phone Number';
@override @override
String get enter_email_or_phone => 'Enter your email or phone number'; String get enter_email_or_phone => 'Enter your phone number';
@override @override
String get password => 'Password'; String get password => 'Password';
@@ -105,4 +105,61 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get profile => 'Profile'; String get profile => 'Profile';
@override
String get forgotPassword => 'Forgot Password';
@override
String get forgotPasswordSubtitle =>
'Please enter your phone number to reset your password.';
@override
String get phoneNumber => 'Phone Number';
@override
String get sendCode => 'Send Code';
@override
String get verifyYourAccount => 'Verify Your Account';
@override
String get verifyAccountSubtitle => 'Weve sent a verification code to';
@override
String get verifyAccount => 'Verify Account';
@override
String get didNotReceiveCode => 'Didnt receive the code?';
@override
String get resend => 'Resend';
@override
String get createNewPassword => 'Create New Password';
@override
String get createNewPasswordSubtitle => 'Please create your new password.';
@override
String get oldPassword => 'Old Password';
@override
String get newPassword => 'New Password';
@override
String get confirmNewPassword => 'Confirm New Password';
@override
String get passwordHint =>
'*Password must have at least 8 characters, including number and symbol.';
@override
String get resetPassword => 'Reset Password';
@override
String get changePasswordSuccess => 'Change Password Success';
@override
String get changePasswordMessage =>
'We have updated your password. Please remember your password, thank you!';
} }

View File

@@ -60,7 +60,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get email_or_phone => 'Номер телефона'; String get email_or_phone => 'Номер телефона';
@override @override
String get enter_email_or_phone => 'Введите свою почту или номер телефона'; String get enter_email_or_phone => 'Введите свою номер телефона';
@override @override
String get password => 'Пароль'; String get password => 'Пароль';
@@ -103,4 +103,61 @@ class AppLocalizationsRu extends AppLocalizations {
@override @override
String get profile => 'Профиль'; String get profile => 'Профиль';
@override
String get forgotPassword => 'Забыли пароль';
@override
String get forgotPasswordSubtitle =>
'Пожалуйста, введите свой номер телефона, чтобы сбросить пароль.';
@override
String get phoneNumber => 'Номер телефона';
@override
String get sendCode => 'Отправить код';
@override
String get verifyYourAccount => 'Подтвердите аккаунт';
@override
String get verifyAccountSubtitle => 'Мы отправили код подтверждения на';
@override
String get verifyAccount => 'Подтвердить';
@override
String get didNotReceiveCode => 'Не получили код?';
@override
String get resend => 'Отправить повторно';
@override
String get createNewPassword => 'Создайте новый пароль';
@override
String get createNewPasswordSubtitle => 'Пожалуйста, создайте новый пароль.';
@override
String get oldPassword => 'Старый пароль';
@override
String get newPassword => 'Новый пароль';
@override
String get confirmNewPassword => 'Подтвердите пароль';
@override
String get passwordHint =>
'*Пароль должен содержать не менее 8 символов, включая цифры и символы.';
@override
String get resetPassword => 'Сбросить пароль';
@override
String get changePasswordSuccess => 'Пароль успешно изменён';
@override
String get changePasswordMessage =>
'Ваш пароль был обновлён. Пожалуйста, запомните его. Спасибо!';
} }

View File

@@ -60,7 +60,7 @@ class AppLocalizationsUz extends AppLocalizations {
String get email_or_phone => 'Telefon raqami'; String get email_or_phone => 'Telefon raqami';
@override @override
String get enter_email_or_phone => 'Email yoki telefon raqamingizni kiriting'; String get enter_email_or_phone => 'Telefon raqamingizni kiriting';
@override @override
String get password => 'Parol'; String get password => 'Parol';
@@ -103,4 +103,63 @@ class AppLocalizationsUz extends AppLocalizations {
@override @override
String get profile => 'Profil'; String get profile => 'Profil';
@override
String get forgotPassword => 'Parolni unutdingizmi';
@override
String get forgotPasswordSubtitle =>
'Parolingizni tiklash uchun telefon raqamingizni kiriting.';
@override
String get phoneNumber => 'Telefon raqami';
@override
String get sendCode => 'Kod yuborish';
@override
String get verifyYourAccount => 'Hisobni tasdiqlang';
@override
String get verifyAccountSubtitle =>
'Tasdiqlash kodi quyidagi raqamga yuborildi';
@override
String get verifyAccount => 'Hisobni tasdiqlash';
@override
String get didNotReceiveCode => 'Kod kelmadimi?';
@override
String get resend => 'Qayta yuborish';
@override
String get createNewPassword => 'Yangi parol yarating';
@override
String get createNewPasswordSubtitle =>
'Iltimos, yangi parolingizni yarating.';
@override
String get oldPassword => 'Eski parol';
@override
String get newPassword => 'Yangi parol';
@override
String get confirmNewPassword => 'Parolni tasdiqlang';
@override
String get passwordHint =>
'*Parol kamida 8 ta belgi, raqam va belgidan iborat bolishi kerak.';
@override
String get resetPassword => 'Parolni tiklash';
@override
String get changePasswordSuccess => 'Parol muvaffaqiyatli ozgartirildi';
@override
String get changePasswordMessage =>
'Parolingiz yangilandi. Iltimos, uni eslab qoling. Rahmat!';
} }

View File

@@ -1,5 +1,8 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:food_delivery_client/feature/auth/presentation/pages/create_new_password_page/create_new_password_page.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/login_page/login_page.dart';
import 'package:food_delivery_client/feature/auth/presentation/pages/verify_account_page/verify_account_page.dart';
import 'package:food_delivery_client/feature/onboarding/presentation/pages/onboarding_page/onboarding_page.dart'; import 'package:food_delivery_client/feature/onboarding/presentation/pages/onboarding_page/onboarding_page.dart';
import 'package:food_delivery_client/feature/onboarding/presentation/pages/splash_page/splash_page.dart'; import 'package:food_delivery_client/feature/onboarding/presentation/pages/splash_page/splash_page.dart';
@@ -30,6 +33,23 @@ class AppRoutes {
path: Routes.main, path: Routes.main,
pageBuilder: (context, state) => CupertinoPage(child: MainPage()), pageBuilder: (context, state) => CupertinoPage(child: MainPage()),
), ),
GoRoute(
path: Routes.forgotPassword,
pageBuilder: (context, state) =>
CupertinoPage(child: ForgotPasswordPage()),
),
GoRoute(
path: Routes.verifyAccount,
pageBuilder: (context, state) => CupertinoPage(
child: VerifyAccountPage(phoneNumber: state.extra as String),
),
),
GoRoute(
path: Routes.createNewPassword,
pageBuilder: (context, state) => CupertinoPage(
child: CreateNewPasswordPage(phoneNumber: state.extra as String),
),
),
], ],
); );
} }

View File

@@ -4,12 +4,7 @@ abstract class Routes {
static const String login = '/login'; static const String login = '/login';
static const String register = '/register'; static const String register = '/register';
static const String main = '/main'; static const String main = '/main';
static const String categories = '/categories'; static const String createNewPassword = '/create-new-password';
static const String filters = '/filters'; static const String forgotPassword = '/forgot-password';
static const String browse = '/browse'; static const String verifyAccount = '/verify-account';
static const String forgotPassword = "/forgot-password";
static const String restaurantsByCategory = '/restaurants-by-category';
static const String verifyPhoneNumber = "/verify-phone-number";
static const String verifyOtpCode = "/verify-otp-code";
static const String resetPassword = "/reset-password";
} }

View File

@@ -8,6 +8,10 @@ class StorageService {
_sharedPreference = await SharedPreferences.getInstance(); _sharedPreference = await SharedPreferences.getInstance();
} }
void setBool({required String key, required bool value}) {
_sharedPreference.setBool(key, value);
}
void setString({required String key, required String value}) { void setString({required String key, required String value}) {
_sharedPreference.setString(key, value); _sharedPreference.setString(key, value);
} }
@@ -25,6 +29,10 @@ class StorageService {
String? getString({required String key}) { String? getString({required String key}) {
return _sharedPreference.getString(key); return _sharedPreference.getString(key);
} }
bool getBool({required String key}) {
return _sharedPreference.getBool(key) ?? false;
}
} }
/* /*

View File

@@ -55,6 +55,8 @@ abstract class AppColors {
static const Color cA7AEC1 = Color(0xFFA7AEC1); static const Color cA7AEC1 = Color(0xFFA7AEC1);
static const Color c151B33 = Color(0xFF151B33); static const Color c151B33 = Color(0xFF151B33);
static const Color cE5E7EB = Color(0xFFE5E7EB); static const Color cE5E7EB = Color(0xFFE5E7EB);
static const Color cE2E8F0 = Color(0xFFE2E8F0);
static const Color cDFE2EB = Color(0xFFDFE2EB);
static const Color c9FB6FA = Color(0xFF9FB6FA);
} }

View File

@@ -13,6 +13,8 @@ abstract class AppIcons {
static const String icCart1 = "$baseUrl/ic_cart1.svg"; static const String icCart1 = "$baseUrl/ic_cart1.svg";
static const String icTransactions = "$baseUrl/ic_transactions.svg"; static const String icTransactions = "$baseUrl/ic_transactions.svg";
static const String icProfile = "$baseUrl/ic_profile.svg"; static const String icProfile = "$baseUrl/ic_profile.svg";
static const String icClock = "$baseUrl/ic_clock.svg";
} }
abstract class AppLightSvgs { abstract class AppLightSvgs {

View File

@@ -10,8 +10,7 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
final Color inActiveColor; final Color inActiveColor;
final Color inActiveColor1; final Color inActiveColor1;
final Color inActiveColor2; final Color inActiveColor2;
final Color rippleColor;
AppThemeColors({ AppThemeColors({
required this.onBoardingColor, required this.onBoardingColor,
@@ -22,7 +21,7 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
required this.inActiveColor, required this.inActiveColor,
required this.inActiveColor1, required this.inActiveColor1,
required this.inActiveColor2, required this.inActiveColor2,
required this.rippleColor,
}); });
static AppThemeColors light = AppThemeColors( static AppThemeColors light = AppThemeColors(
@@ -33,7 +32,8 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
borderColor: AppColors.cE2E4EA, borderColor: AppColors.cE2E4EA,
inActiveColor: AppColors.cE2E4EA, inActiveColor: AppColors.cE2E4EA,
inActiveColor1: AppColors.cA9A9A9, inActiveColor1: AppColors.cA9A9A9,
inActiveColor2: AppColors.cE5E7EB inActiveColor2: AppColors.cE5E7EB,
rippleColor: AppColors.c9FB6FA,
); );
static AppThemeColors dark = AppThemeColors( static AppThemeColors dark = AppThemeColors(
@@ -44,7 +44,8 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
borderColor: AppColors.c292F3D, borderColor: AppColors.c292F3D,
inActiveColor: AppColors.c292F3D, inActiveColor: AppColors.c292F3D,
inActiveColor1: AppColors.c626262, inActiveColor1: AppColors.c626262,
inActiveColor2: AppColors.c292F3D inActiveColor2: AppColors.c292F3D,
rippleColor: AppColors.cFF6F00,
); );
@override @override
@@ -57,7 +58,7 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
Color? inActiveColor, Color? inActiveColor,
Color? inActiveColor1, Color? inActiveColor1,
Color? inActiveColor2, Color? inActiveColor2,
Color? rippleColor,
}) { }) {
return AppThemeColors( return AppThemeColors(
onBoardingColor: onBoardingColor ?? this.onBoardingColor, onBoardingColor: onBoardingColor ?? this.onBoardingColor,
@@ -67,7 +68,8 @@ class AppThemeColors extends ThemeExtension<AppThemeColors> {
borderColor: borderColor ?? this.borderColor, borderColor: borderColor ?? this.borderColor,
inActiveColor: inActiveColor ?? this.inActiveColor, inActiveColor: inActiveColor ?? this.inActiveColor,
inActiveColor1: inActiveColor1 ?? this.inActiveColor1, inActiveColor1: inActiveColor1 ?? this.inActiveColor1,
inActiveColor2: inActiveColor2??this.inActiveColor2 inActiveColor2: inActiveColor2 ?? this.inActiveColor2,
rippleColor: rippleColor ?? this.rippleColor,
); );
} }

View File

@@ -5,6 +5,8 @@ import 'package:food_delivery_client/core/core.dart';
class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> { class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
//Regular fonts //Regular fonts
final TextStyle size14Regular; final TextStyle size14Regular;
final TextStyle size14Regular1;
//Medium //Medium
@@ -14,6 +16,8 @@ class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
final TextStyle size16SemiBold; final TextStyle size16SemiBold;
final TextStyle size24SemiBold; final TextStyle size24SemiBold;
final TextStyle size24SemiBold1;
//Bold //Bold
@@ -25,9 +29,11 @@ class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
AppThemeTextStyles({ AppThemeTextStyles({
required this.size14Regular, required this.size14Regular,
required this.size14Regular1,
required this.size16Medium, required this.size16Medium,
required this.size16SemiBold, required this.size16SemiBold,
required this.size24SemiBold, required this.size24SemiBold,
required this.size24SemiBold1,
required this.size14Bold, required this.size14Bold,
required this.size24Bold, required this.size24Bold,
required this.size64Black, required this.size64Black,
@@ -47,6 +53,13 @@ class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
fontFamily: _fontRegular, fontFamily: _fontRegular,
color: AppColors.c151B33, color: AppColors.c151B33,
), ),
size14Regular1: TextStyle(
fontSize: 14,
height: 1.6,
fontWeight: FontWeight.w400,
fontFamily: _fontRegular,
color: AppColors.c151B33,
),
size16Medium: TextStyle( size16Medium: TextStyle(
fontSize: 16, fontSize: 16,
height: 1.3, height: 1.3,
@@ -67,6 +80,14 @@ class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
fontFamily: _fontSemiBold, fontFamily: _fontSemiBold,
color: AppColors.c1A202C, color: AppColors.c1A202C,
), ),
size24SemiBold1: TextStyle(
fontSize: 24,
height: 1.25,
letterSpacing: -.48,
fontWeight: FontWeight.w600,
fontFamily: _fontSemiBold,
color: AppColors.c000000,
),
size14Bold: TextStyle( size14Bold: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
@@ -95,6 +116,13 @@ class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
fontFamily: _fontRegular, fontFamily: _fontRegular,
color: AppColors.cFFFFFF, color: AppColors.cFFFFFF,
), ),
size14Regular1: TextStyle(
fontSize: 14,
height: 1.6,
fontWeight: FontWeight.w400,
fontFamily: _fontRegular,
color: AppColors.cA9A9A9,
),
size16Medium: TextStyle( size16Medium: TextStyle(
fontSize: 16, fontSize: 16,
height: 1.3, height: 1.3,
@@ -115,6 +143,14 @@ class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
fontFamily: _fontSemiBold, fontFamily: _fontSemiBold,
color: AppColors.cFFFFFF, color: AppColors.cFFFFFF,
), ),
size24SemiBold1: TextStyle(
fontSize: 24,
height: 1.25,
letterSpacing: -.48,
fontWeight: FontWeight.w600,
fontFamily: _fontSemiBold,
color: AppColors.cFFFFFF,
),
size14Bold: TextStyle( size14Bold: TextStyle(
fontSize: 14, fontSize: 14,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
@@ -138,18 +174,22 @@ class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
@override @override
ThemeExtension<AppThemeTextStyles> copyWith({ ThemeExtension<AppThemeTextStyles> copyWith({
TextStyle? size14Regular, TextStyle? size14Regular,
TextStyle? size14Regular1,
TextStyle? size16Medium, TextStyle? size16Medium,
TextStyle? size16SemiBold, TextStyle? size16SemiBold,
TextStyle? size24SemiBold, TextStyle? size24SemiBold,
TextStyle? size24SemiBold1,
TextStyle? size14Bold, TextStyle? size14Bold,
TextStyle? size24Bold, TextStyle? size24Bold,
TextStyle? size64Black, TextStyle? size64Black,
}) { }) {
return AppThemeTextStyles( return AppThemeTextStyles(
size14Regular: size14Regular ?? this.size14Regular, size14Regular: size14Regular ?? this.size14Regular,
size14Regular1: size14Regular1 ?? this.size14Regular1,
size16Medium: size16Medium ?? this.size16Medium, size16Medium: size16Medium ?? this.size16Medium,
size16SemiBold: size16SemiBold ?? this.size16SemiBold, size16SemiBold: size16SemiBold ?? this.size16SemiBold,
size24SemiBold: size24SemiBold ?? this.size24SemiBold, size24SemiBold: size24SemiBold ?? this.size24SemiBold,
size24SemiBold1: size24SemiBold1 ?? this.size24SemiBold1,
size14Bold: size14Bold ?? this.size14Bold, size14Bold: size14Bold ?? this.size14Bold,
size24Bold: size24Bold ?? this.size24Bold, size24Bold: size24Bold ?? this.size24Bold,
size64Black: size64Black ?? this.size64Black, size64Black: size64Black ?? this.size64Black,
@@ -164,9 +204,11 @@ class AppThemeTextStyles extends ThemeExtension<AppThemeTextStyles> {
if (other is! AppThemeTextStyles) return this; if (other is! AppThemeTextStyles) return this;
return AppThemeTextStyles( return AppThemeTextStyles(
size14Regular: TextStyle.lerp(size14Regular, other.size14Regular, t)!, size14Regular: TextStyle.lerp(size14Regular, other.size14Regular, t)!,
size14Regular1: TextStyle.lerp(size14Regular1, other.size14Regular1, t)!,
size16Medium: TextStyle.lerp(size16Medium, other.size16Medium, t)!, size16Medium: TextStyle.lerp(size16Medium, other.size16Medium, t)!,
size16SemiBold: TextStyle.lerp(size16SemiBold, other.size16SemiBold, t)!, size16SemiBold: TextStyle.lerp(size16SemiBold, other.size16SemiBold, t)!,
size24SemiBold: TextStyle.lerp(size24SemiBold, other.size24SemiBold, t)!, size24SemiBold: TextStyle.lerp(size24SemiBold, other.size24SemiBold, t)!,
size24SemiBold1: TextStyle.lerp(size24SemiBold1, other.size24SemiBold1, t)!,
size14Bold: TextStyle.lerp(size14Bold, other.size14Bold, t)!, size14Bold: TextStyle.lerp(size14Bold, other.size14Bold, t)!,
size24Bold: TextStyle.lerp(size24Bold, other.size24Bold, t)!, size24Bold: TextStyle.lerp(size24Bold, other.size24Bold, t)!,
size64Black: TextStyle.lerp(size64Black, other.size64Black, t)!, size64Black: TextStyle.lerp(size64Black, other.size64Black, t)!,

View File

@@ -0,0 +1,14 @@
import 'package:food_delivery_client/feature/auth/presentation/pages/create_new_password_page/widgets/create_new_password_body.dart';
import '../../../../../food_delivery_client.dart';
class CreateNewPasswordPage extends StatelessWidget {
const CreateNewPasswordPage({super.key, required this.phoneNumber});
final String phoneNumber;
@override
Widget build(BuildContext context) {
return CreateNewPasswordBody(phoneNumber: phoneNumber );
}
}

View File

@@ -0,0 +1,95 @@
import '../../../../../../food_delivery_client.dart';
class ChangePasswordSuccessPassword extends StatelessWidget {
const ChangePasswordSuccessPassword({super.key});
void show(BuildContext context) {
showModalBottomSheet(
context: context,
backgroundColor: AppColors.cTransparent,
enableDrag: false,
isDismissible: false,
useSafeArea: false,
builder: (context) => Wrap(children: [this]),
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: context.w,
child: DecoratedBox(
decoration: BoxDecoration(
color: context.theme.scaffoldBackgroundColor,
borderRadius: AppUtils.kBorderRadius30,
),
child: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
10.verticalSpace,
SizedBox(
height: 6,
width: 60,
child: DecoratedBox(
decoration: BoxDecoration(
color: AppColors.cDFE2EB,
borderRadius: AppUtils.kBorderRadius10,
),
),
),
54.verticalSpace,
SizedBox(
height: 162,
width: 162,
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(160),
color: context.appThemeColors.rippleColor.newWithOpacity(
.2,
),
),
child: Center(
child: SizedBox(
height: 103.74,
width: 103.74,
child: DecoratedBox(
decoration: BoxDecoration(
color: AppColors.cFF6F00,
borderRadius: BorderRadius.circular(100),
),
child: Center(
child: SvgPicture.asset(AppIcons.icClock),
),
),
),
),
),
),
54.verticalSpace,
Text(
context.loc.changePasswordSuccess,
style: context.appThemeTextStyles.size24Bold,
textAlign: TextAlign.center,
),
14.verticalSpace,
Text(
context.loc.changePasswordMessage,
style: context.appThemeTextStyles.size14Regular,
textAlign: TextAlign.center,
),
65.verticalSpace,
AppButton(
name: context.loc.login,
onPressed: () {
context.go(Routes.login);
},
),
15.verticalSpace,
],
).paddingSymmetric(horizontal: 24),
),
),
);
}
}

View File

@@ -0,0 +1,126 @@
import 'package:food_delivery_client/core/helpers/validator_helpers.dart';
import 'package:food_delivery_client/feature/common/presentation/widgets/app_text_form_field.dart';
import '../../../../../../food_delivery_client.dart';
import 'change_password_success.dart';
class CreateNewPasswordBody extends StatefulWidget {
const CreateNewPasswordBody({super.key, required this.phoneNumber});
final String phoneNumber;
@override
State<CreateNewPasswordBody> createState() => _CreateNewPasswordBodyState();
}
class _CreateNewPasswordBodyState extends State<CreateNewPasswordBody> {
late TextEditingController _passwordController;
late TextEditingController _repeatPasswordController;
final _formKey = GlobalKey<FormState>();
bool _isValid = false;
void _validateForm() {
final isValid = _formKey.currentState?.validate() ?? false;
if (_isValid != isValid) {
setState(() {
_isValid = isValid;
});
}
}
@override
void initState() {
_passwordController = TextEditingController();
_repeatPasswordController = TextEditingController();
_passwordController.addListener(_validateForm);
_repeatPasswordController.addListener(_validateForm);
super.initState();
}
@override
void dispose() {
_passwordController.removeListener(_validateForm);
_repeatPasswordController.removeListener(_validateForm);
_passwordController.dispose();
_repeatPasswordController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: WLayout(
child: Scaffold(
resizeToAvoidBottomInset: true,
body: SingleChildScrollView(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
physics: const NeverScrollableScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
30.verticalSpace,
WBackButton(),
20.verticalSpace,
Text(
context.loc.createNewPassword,
style: context.appThemeTextStyles.size24Bold,
),
8.verticalSpace,
Text(
context.loc.createNewPasswordSubtitle,
style: context.appThemeTextStyles.size14Regular,
),
40.verticalSpace,
Text(
context.loc.newPassword,
style: context.appThemeTextStyles.size16Medium,
),
10.verticalSpace,
AppTextFormField(
obscureText: true,
controller: _passwordController,
keyBoardType: TextInputType.visiblePassword,
hintText: context.loc.enter_password,
validator: (value) {
return Validators.validatePassword(
_passwordController.text.trim(),
);
},
),
16.verticalSpace,
Text(
context.loc.confirmNewPassword,
style: context.appThemeTextStyles.size16Medium,
),
10.verticalSpace,
AppTextFormField(
obscureText: true,
controller: _repeatPasswordController,
keyBoardType: TextInputType.visiblePassword,
hintText: context.loc.enter_password,
validator: (value) {
return Validators.validateRepeatPassword(
_repeatPasswordController.text.trim(),
_passwordController.text.trim(),
);
},
),
130.verticalSpace,
AppButton(
name: context.loc.resetPassword,
isActive: _isValid,
onPressed: () {
ChangePasswordSuccessPassword().show(context);
},
),
],
).paddingSymmetric(horizontal: 24),
),
),
),
);
}
}

View File

@@ -0,0 +1,12 @@
import 'package:food_delivery_client/feature/auth/presentation/pages/forgot_password_page/widgets/forgot_password_body.dart';
import '../../../../../food_delivery_client.dart';
class ForgotPasswordPage extends StatelessWidget {
const ForgotPasswordPage({super.key});
@override
Widget build(BuildContext context) {
return ForgotPasswordBody();
}
}

View File

@@ -0,0 +1,117 @@
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/common/presentation/widgets/app_text_form_field.dart';
import '../../../../../../food_delivery_client.dart';
class ForgotPasswordBody extends StatefulWidget {
const ForgotPasswordBody({super.key});
@override
State<ForgotPasswordBody> createState() => _ForgotPasswordBodyState();
}
class _ForgotPasswordBodyState extends State<ForgotPasswordBody> {
late final TextEditingController _phoneNumberController;
final _formKey = GlobalKey<FormState>();
bool _isValid = false;
void _validateForm() {
final isValid = _formKey.currentState?.validate() ?? false;
if (isValid != _isValid) {
setState(() {
_isValid = isValid;
});
}
}
@override
void initState() {
_phoneNumberController = TextEditingController();
_phoneNumberController.addListener(_validateForm);
super.initState();
}
@override
void dispose() {
_phoneNumberController.removeListener(_validateForm);
_phoneNumberController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: WLayout(
child: Scaffold(
resizeToAvoidBottomInset: true,
body: SingleChildScrollView(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
30.verticalSpace,
WBackButton(),
20.verticalSpace,
Text(
context.loc.forgotPassword,
style: context.appThemeTextStyles.size24Bold,
),
8.verticalSpace,
Text(
context.loc.forgotPasswordSubtitle,
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.cA7AEC1,
height: 1.6,
),
).paddingOnly(right: 50),
30.verticalSpace,
Text(
context.loc.phoneNumber,
style: context.appThemeTextStyles.size16Medium,
),
10.verticalSpace,
AppTextFormField(
controller: _phoneNumberController,
keyBoardType: TextInputType.number,
hintText: context.loc.enter_email_or_phone,
prefixIcon: Text(
"+ 998",
style: context.appThemeTextStyles.size14Regular,
),
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
Formatters.phoneFormatter,
LengthLimitingTextInputFormatter(12),
],
validator: (value) {
return Validators.validatePhoneNumber(
_phoneNumberController.text.trim(),
);
},
),
100.verticalSpace,
AppButton(
name: context.loc.sendCode,
isActive: _isValid,
onPressed: () {
context.push(
Routes.verifyAccount,
extra:
"+998${_phoneNumberController.text.trim().replaceAll(" ", "")}",
);
if (_formKey.currentState?.validate() ?? false) {}
},
),
],
).paddingSymmetric(horizontal: 24),
),
),
),
);
}
}

View File

@@ -108,7 +108,12 @@ class _WLoginBodyState extends State<WLoginBody> {
), ),
10.verticalSpace, 10.verticalSpace,
AppTextFormField( AppTextFormField(
prefixIcon: Text("+ 998"), prefixIcon: Text(
"+ 998",
style: context
.appThemeTextStyles
.size14Regular,
),
controller: _phoneNumberController, controller: _phoneNumberController,
hintText: context.loc.enter_email_or_phone, hintText: context.loc.enter_email_or_phone,
inputFormatters: [ inputFormatters: [
@@ -143,7 +148,9 @@ class _WLoginBodyState extends State<WLoginBody> {
Align( Align(
alignment: AlignmentGeometry.centerRight, alignment: AlignmentGeometry.centerRight,
child: TextButton( child: TextButton(
onPressed: () {}, onPressed: () {
context.push(Routes.forgotPassword);
},
child: Text( child: Text(
context.loc.forgot_password, context.loc.forgot_password,
style: AppTextStyles.size14Regular style: AppTextStyles.size14Regular

View File

@@ -0,0 +1,13 @@
import 'package:food_delivery_client/feature/auth/presentation/pages/verify_account_page/widgets/verify_account_body.dart';
import '../../../../../food_delivery_client.dart';
class VerifyAccountPage extends StatelessWidget {
const VerifyAccountPage({super.key, required this.phoneNumber});
final String phoneNumber;
@override
Widget build(BuildContext context) {
return VerifyAccountBody(phoneNumber: phoneNumber);
}
}

View File

@@ -0,0 +1,167 @@
import 'package:food_delivery_client/core/helpers/string_helpers.dart';
import 'package:food_delivery_client/core/helpers/validator_helpers.dart';
import 'package:pinput/pinput.dart';
import '../../../../../../food_delivery_client.dart';
class VerifyAccountBody extends StatefulWidget {
const VerifyAccountBody({super.key, required this.phoneNumber});
final String phoneNumber;
@override
State<VerifyAccountBody> createState() => _VerifyAccountBodyState();
}
class _VerifyAccountBodyState extends State<VerifyAccountBody> {
late TextEditingController _otpController;
final _formKey = GlobalKey<FormState>();
bool _isValid = false;
void _formValidator() {
final isValid = _formKey.currentState?.validate() ?? false;
if (_isValid != isValid) {
setState(() {
_isValid = isValid;
});
}
}
@override
void initState() {
_otpController = TextEditingController();
_otpController.addListener(_formValidator);
super.initState();
}
@override
void dispose() {
_otpController.removeListener(_formValidator);
_otpController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final defaultPinTheme = PinTheme(
height: 56,
width: 56,
textStyle: context.appThemeTextStyles.size24SemiBold1,
decoration: BoxDecoration(
border: Border.all(
color: context.appThemeColors.borderColor,
style: BorderStyle.solid,
width: 1,
),
color: context.theme.scaffoldBackgroundColor,
borderRadius: AppUtils.kBorderRadius12,
),
);
return Form(
key: _formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: WLayout(
child: Scaffold(
resizeToAvoidBottomInset: true,
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
30.verticalSpace,
WBackButton(),
20.verticalSpace,
Text(
context.loc.verifyYourAccount,
style: context.appThemeTextStyles.size24Bold,
),
8.verticalSpace,
RichText(
text: TextSpan(
text: context.loc.verifyAccountSubtitle,
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.cA7AEC1,
height: 1.6,
),
children: [
TextSpan(text: " "),
TextSpan(
text: StringHelpers.formatUzbekPhoneNumber(
widget.phoneNumber,
),
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.cFF6F00,
),
),
],
),
),
50.verticalSpace,
Pinput(
autofocus: true,
enabled: true,
length: 5,
controller: _otpController,
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
keyboardType: TextInputType.number,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(5),
],
defaultPinTheme: defaultPinTheme,
focusedPinTheme: defaultPinTheme.copyWith(
decoration: defaultPinTheme.decoration?.copyWith(
border: Border.all(color: AppColors.cFF6F00),
),
),
validator: (value) {
return Validators.validateOtpFields(
_otpController.text.trim(),
);
},
),
90.verticalSpace,
AppButton(
isActive: _isValid,
name: context.loc.verifyAccount,
onPressed: () {
if (_formKey.currentState?.validate() ?? false) {
context.push(
Routes.createNewPassword,
extra: widget.phoneNumber,
);
}
},
),
20.verticalSpace,
Align(
alignment: AlignmentGeometry.center,
child: RichText(
text: TextSpan(
text: context.loc.didNotReceiveCode,
style: context.appThemeTextStyles.size14Regular1,
children: [
WidgetSpan(
baseline: TextBaseline.alphabetic,
alignment: PlaceholderAlignment.baseline,
child: TextButton(
onPressed: () {},
child: Text(
context.loc.resend,
style: AppTextStyles.size14Regular.copyWith(
color: AppColors.cFF6F00,
),
),
),
),
],
),
),
),
],
).paddingSymmetric(horizontal: 24),
),
),
),
);
}
}

View File

@@ -0,0 +1,37 @@
import 'package:food_delivery_client/food_delivery_client.dart';
part 'theme_event.dart';
part 'theme_state.dart';
part 'theme_bloc.freezed.dart';
@injectable
class ThemeBloc extends Bloc<ThemeEvent, ThemeState> {
final StorageService _storageService;
ThemeBloc(this._storageService) : super(const ThemeState()) {
on<_Started>(_onStarted);
on<_Changed>(_onChanged);
}
void _onStarted(_Started event, Emitter<ThemeState> emit) {
final isDark = _storageService.getBool(key: AppLocaleKeys.theme);
if (isDark) {
emit(state.copyWith(themeMode: ThemeMode.dark));
} else {
emit(state.copyWith(themeMode: ThemeMode.light));
}
}
void _onChanged(_Changed event, Emitter<ThemeState> emit) {
if (state.themeMode == ThemeMode.light) {
_storageService.setBool(key: AppLocaleKeys.theme, value: true);
emit(state.copyWith(themeMode: ThemeMode.dark));
} else {
_storageService.setBool(key: AppLocaleKeys.theme, value: false);
emit(state.copyWith(themeMode: ThemeMode.light));
}
}
}

View File

@@ -0,0 +1,501 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
part of 'theme_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ThemeEvent {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ThemeEvent);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'ThemeEvent()';
}
}
/// @nodoc
class $ThemeEventCopyWith<$Res> {
$ThemeEventCopyWith(ThemeEvent _, $Res Function(ThemeEvent) __);
}
/// Adds pattern-matching-related methods to [ThemeEvent].
extension ThemeEventPatterns on ThemeEvent {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( _Started value)? started,TResult Function( _Changed value)? changed,required TResult orElse(),}){
final _that = this;
switch (_that) {
case _Started() when started != null:
return started(_that);case _Changed() when changed != null:
return changed(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( _Started value) started,required TResult Function( _Changed value) changed,}){
final _that = this;
switch (_that) {
case _Started():
return started(_that);case _Changed():
return changed(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( _Started value)? started,TResult? Function( _Changed value)? changed,}){
final _that = this;
switch (_that) {
case _Started() when started != null:
return started(_that);case _Changed() when changed != null:
return changed(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function()? started,TResult Function()? changed,required TResult orElse(),}) {final _that = this;
switch (_that) {
case _Started() when started != null:
return started();case _Changed() when changed != null:
return changed();case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function() started,required TResult Function() changed,}) {final _that = this;
switch (_that) {
case _Started():
return started();case _Changed():
return changed();case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function()? started,TResult? Function()? changed,}) {final _that = this;
switch (_that) {
case _Started() when started != null:
return started();case _Changed() when changed != null:
return changed();case _:
return null;
}
}
}
/// @nodoc
class _Started implements ThemeEvent {
const _Started();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Started);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'ThemeEvent.started()';
}
}
/// @nodoc
class _Changed implements ThemeEvent {
const _Changed();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Changed);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'ThemeEvent.changed()';
}
}
/// @nodoc
mixin _$ThemeState {
ThemeMode get themeMode;
/// Create a copy of ThemeState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ThemeStateCopyWith<ThemeState> get copyWith => _$ThemeStateCopyWithImpl<ThemeState>(this as ThemeState, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ThemeState&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode));
}
@override
int get hashCode => Object.hash(runtimeType,themeMode);
@override
String toString() {
return 'ThemeState(themeMode: $themeMode)';
}
}
/// @nodoc
abstract mixin class $ThemeStateCopyWith<$Res> {
factory $ThemeStateCopyWith(ThemeState value, $Res Function(ThemeState) _then) = _$ThemeStateCopyWithImpl;
@useResult
$Res call({
ThemeMode themeMode
});
}
/// @nodoc
class _$ThemeStateCopyWithImpl<$Res>
implements $ThemeStateCopyWith<$Res> {
_$ThemeStateCopyWithImpl(this._self, this._then);
final ThemeState _self;
final $Res Function(ThemeState) _then;
/// Create a copy of ThemeState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? themeMode = null,}) {
return _then(_self.copyWith(
themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as ThemeMode,
));
}
}
/// Adds pattern-matching-related methods to [ThemeState].
extension ThemeStatePatterns on ThemeState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ThemeState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ThemeState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ThemeState value) $default,){
final _that = this;
switch (_that) {
case _ThemeState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ThemeState value)? $default,){
final _that = this;
switch (_that) {
case _ThemeState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( ThemeMode themeMode)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ThemeState() when $default != null:
return $default(_that.themeMode);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( ThemeMode themeMode) $default,) {final _that = this;
switch (_that) {
case _ThemeState():
return $default(_that.themeMode);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( ThemeMode themeMode)? $default,) {final _that = this;
switch (_that) {
case _ThemeState() when $default != null:
return $default(_that.themeMode);case _:
return null;
}
}
}
/// @nodoc
class _ThemeState implements ThemeState {
const _ThemeState({this.themeMode = ThemeMode.light});
@override@JsonKey() final ThemeMode themeMode;
/// Create a copy of ThemeState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ThemeStateCopyWith<_ThemeState> get copyWith => __$ThemeStateCopyWithImpl<_ThemeState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ThemeState&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode));
}
@override
int get hashCode => Object.hash(runtimeType,themeMode);
@override
String toString() {
return 'ThemeState(themeMode: $themeMode)';
}
}
/// @nodoc
abstract mixin class _$ThemeStateCopyWith<$Res> implements $ThemeStateCopyWith<$Res> {
factory _$ThemeStateCopyWith(_ThemeState value, $Res Function(_ThemeState) _then) = __$ThemeStateCopyWithImpl;
@override @useResult
$Res call({
ThemeMode themeMode
});
}
/// @nodoc
class __$ThemeStateCopyWithImpl<$Res>
implements _$ThemeStateCopyWith<$Res> {
__$ThemeStateCopyWithImpl(this._self, this._then);
final _ThemeState _self;
final $Res Function(_ThemeState) _then;
/// Create a copy of ThemeState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? themeMode = null,}) {
return _then(_ThemeState(
themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable
as ThemeMode,
));
}
}
// dart format on

View File

@@ -0,0 +1,8 @@
part of 'theme_bloc.dart';
@freezed
class ThemeEvent with _$ThemeEvent {
const factory ThemeEvent.started() = _Started;
const factory ThemeEvent.changed() = _Changed;
}

View File

@@ -0,0 +1,7 @@
part of 'theme_bloc.dart';
@freezed
abstract class ThemeState with _$ThemeState {
const factory ThemeState({@Default(ThemeMode.light) ThemeMode themeMode}) =
_ThemeState;
}

View File

@@ -92,7 +92,7 @@ class _AppTextFormFieldState extends State<AppTextFormField> {
widget.hintTextStyle ?? widget.hintTextStyle ??
context.appThemeTextStyles.size14Regular.copyWith( context.appThemeTextStyles.size14Regular.copyWith(
color: AppColors.cA7AEC1, color: AppColors.cA7AEC1,
), ),
suffixIcon: widget.obscureText suffixIcon: widget.obscureText
? IconButton( ? IconButton(
onPressed: () { onPressed: () {
@@ -101,8 +101,9 @@ class _AppTextFormFieldState extends State<AppTextFormField> {
}); });
}, },
icon: SvgPicture.asset( icon: SvgPicture.asset(
visibility ? AppIcons.icVisibility : AppIcons.icVisibilityOff, visibility
color: AppColors.c000000, ? AppIcons.icVisibility
: AppIcons.icVisibilityOff,
), ),
) )
: widget.suffixIcon, : widget.suffixIcon,

View File

@@ -6,7 +6,9 @@ class WBackButton extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return InkWell( return InkWell(
onTap: () {}, onTap: () {
context.pop();
},
borderRadius: AppUtils.kBorderRadius22, borderRadius: AppUtils.kBorderRadius22,
child: Ink( child: Ink(
height: 44, height: 44,
@@ -19,7 +21,7 @@ class WBackButton extends StatelessWidget {
color: AppColors.cA7AEC1.newWithOpacity(.3), color: AppColors.cA7AEC1.newWithOpacity(.3),
offset: const Offset(0, 4), offset: const Offset(0, 4),
blurRadius: 80, blurRadius: 80,
), ),
], ],
), ),
child: SvgPicture.asset( child: SvgPicture.asset(

View File

@@ -1,21 +1,35 @@
import '../../../../../food_delivery_client.dart'; import '../../../../../food_delivery_client.dart';
import '../../../common/presentation/blocs/theme_bloc/theme_bloc.dart';
class ProfilePage extends StatelessWidget { class ProfilePage extends StatelessWidget {
const ProfilePage({super.key}); const ProfilePage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return BlocBuilder<ThemeBloc, ThemeState>(
body: Column( builder: (context, state) {
crossAxisAlignment: CrossAxisAlignment.center, return Scaffold(
mainAxisAlignment: MainAxisAlignment.center, body: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.center,
Text("Home"), mainAxisAlignment: MainAxisAlignment.center,
GradientSwitch(value:false, onChanged: (value) { children: [
print(value); Text("Home"),
}), Row(
], mainAxisAlignment: MainAxisAlignment.spaceBetween,
), children: [
Text("Dark mode"),
GradientSwitch(
value: state.themeMode == ThemeMode.dark,
onChanged: (value) {
context.read<ThemeBloc>().add(ThemeEvent.changed());
},
),
],
).paddingSymmetric(horizontal: 20),
],
),
);
},
); );
} }
} }

View File

@@ -1,5 +1,5 @@
import 'package:food_delivery_client/feature/common/presentation/blocs/theme_bloc/theme_bloc.dart';
import 'package:toastification/toastification.dart'; import 'package:toastification/toastification.dart';
import 'food_delivery_client.dart'; import 'food_delivery_client.dart';
void main() { void main() {
@@ -13,7 +13,13 @@ void main() {
Bloc.observer = AppBlocObserver(); Bloc.observer = AppBlocObserver();
await configureDependencies(); await configureDependencies();
runApp( runApp(
BlocProvider(create: (context) => sl<LanguageBloc>(), child: MyApp()), MultiBlocProvider(
providers: [
BlocProvider(create: (context) => sl<LanguageBloc>()),
BlocProvider(create: (context) => sl<ThemeBloc>()),
],
child: MyApp(),
),
); );
}, },
(error, stack) { (error, stack) {
@@ -32,46 +38,55 @@ class MyApp extends StatefulWidget {
class _MyAppState extends State<MyApp> { class _MyAppState extends State<MyApp> {
@override @override
void initState() { void initState() {
SystemChrome.setPreferredOrientations([ WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
DeviceOrientation.portraitUp, SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitDown, DeviceOrientation.portraitUp,
]); DeviceOrientation.portraitDown,
]);
context.read<LanguageBloc>().add(LanguageEvent.started());
context.read<ThemeBloc>().add(ThemeEvent.started());
});
super.initState(); super.initState();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<LanguageBloc, LanguageState>( return BlocBuilder<LanguageBloc, LanguageState>(
bloc: context.read<LanguageBloc>()..add(LanguageEvent.started()),
builder: (context, state) { builder: (context, state) {
return ToastificationWrapper( return BlocBuilder<ThemeBloc, ThemeState>(
child: MaterialApp.router( builder: (context, themeState) {
title: AppLocaleKeys.appName, return ToastificationWrapper(
debugShowCheckedModeBanner: false, child: MaterialApp.router(
theme: AppTheme.lightTheme, title: AppLocaleKeys.appName,
darkTheme: AppTheme.darkTheme, debugShowCheckedModeBanner: false,
themeMode: ThemeMode.light, theme: AppTheme.lightTheme,
routerConfig: sl<AppRoutes>().router, darkTheme: AppTheme.darkTheme,
locale: state.currentLocale, themeMode: ThemeMode.light,
supportedLocales: L10n.locales, // themeMode: themeState.themeMode,
localizationsDelegates: [ routerConfig: sl<AppRoutes>().router,
AppLocalizations.delegate, locale: state.currentLocale,
GlobalMaterialLocalizations.delegate, supportedLocales: L10n.locales,
GlobalWidgetsLocalizations.delegate, localizationsDelegates: [
GlobalCupertinoLocalizations.delegate, AppLocalizations.delegate,
], GlobalMaterialLocalizations.delegate,
builder: (context, child) => GestureDetector( GlobalWidgetsLocalizations.delegate,
onTap: () { GlobalCupertinoLocalizations.delegate,
FocusManager.instance.primaryFocus?.unfocus(); ],
}, builder: (context, child) => GestureDetector(
child: MediaQuery( onTap: () {
data: MediaQuery.of( FocusManager.instance.primaryFocus?.unfocus();
context, },
).copyWith(textScaler: const TextScaler.linear(1)), child: MediaQuery(
child: child!, data: MediaQuery.of(
context,
).copyWith(textScaler: const TextScaler.linear(1)),
child: child!,
),
),
), ),
), );
), },
); );
}, },
); );