Merge branch 'main' into meet
This commit is contained in:
7
assets/images/contact_admin_screen/svg/mail_icon.svg
Normal file
7
assets/images/contact_admin_screen/svg/mail_icon.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-inside-1_2028_2122" fill="white">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 8.08789L13.4757 13.3593C12.6195 14.3307 11.4193 14.3307 10.5631 13.3593L6 8.08789"/>
|
||||
</mask>
|
||||
<path d="M19.1383 9.06481C19.6778 8.43617 19.6056 7.48917 18.9769 6.94964C18.3483 6.4101 17.4013 6.48233 16.8617 7.11097L19.1383 9.06481ZM13.4757 13.3593L14.601 14.3511C14.6054 14.3462 14.6097 14.3412 14.614 14.3362L13.4757 13.3593ZM10.5631 13.3593L9.42895 14.3411L9.43781 14.3511L10.5631 13.3593ZM7.13412 7.10617C6.59192 6.47981 5.64463 6.41158 5.01828 6.95378C4.39192 7.49597 4.32369 8.44326 4.86588 9.06961L7.13412 7.10617ZM16.8617 7.11097L12.3375 12.3824L14.614 14.3362L19.1383 9.06481L16.8617 7.11097ZM12.3504 12.3675C12.1561 12.5879 12.0314 12.5879 12.0194 12.5879C12.0075 12.5879 11.8827 12.5879 11.6884 12.3675L9.43781 14.3511C10.0997 15.1021 11.0032 15.5879 12.0194 15.5879C13.0357 15.5879 13.9391 15.1021 14.601 14.3511L12.3504 12.3675ZM11.6972 12.3776L7.13412 7.10617L4.86588 9.06961L9.42899 14.341L11.6972 12.3776Z" fill="#05391B" mask="url(#path-1-inside-1_2028_2122)"/>
|
||||
<path d="M22.2416 8.60756C22.2406 8.62349 22.2401 8.63944 22.2401 8.6554V15.5204C22.2401 15.5363 22.2406 15.5523 22.2416 15.5682C22.319 16.7783 21.8584 17.977 20.945 18.8831C20.0301 19.7906 18.7409 20.3228 17.3734 20.3379H6.59176C3.68522 20.3379 1.75 18.1955 1.75 15.5204V8.6554C1.75 5.98031 3.68522 3.83789 6.59176 3.83789H17.3734C18.7409 3.85294 20.0301 4.3852 20.945 5.29268C21.8584 6.19874 22.319 7.39744 22.2416 8.60756Z" stroke="#05391B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
3
assets/images/contact_admin_screen/svg/phone_icon.svg
Normal file
3
assets/images/contact_admin_screen/svg/phone_icon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.99033 4.87254C3.30665 4.34878 5.0495 2.44376 6.29322 2.50127C6.665 2.53208 6.99364 2.75699 7.26067 3.01784C7.87379 3.61656 9.62897 5.88101 9.72859 6.35753C9.97096 7.52621 8.57833 8.1999 9.00454 9.37783C10.0911 12.0366 11.9634 13.9088 14.6233 14.9943C15.8003 15.4205 16.474 14.0279 17.6428 14.2713C18.1183 14.3709 20.3839 16.126 20.9826 16.7391C21.2425 17.0051 21.4684 17.3347 21.4992 17.7065C21.5454 19.0159 19.5222 20.7833 19.1278 21.0092C18.1974 21.6747 16.9834 21.6634 15.5035 20.9753C11.3739 19.2572 4.77426 12.7822 3.02422 8.49669C2.35461 7.02505 2.30839 5.80297 2.99033 4.87254Z" stroke="#05391B" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 826 B |
BIN
assets/images/language_screen/png/info_icon.png
Normal file
BIN
assets/images/language_screen/png/info_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
11
assets/images/language_screen/svg/info_icon.svg
Normal file
11
assets/images/language_screen/svg/info_icon.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g opacity="0.6">
|
||||
<rect x="7.5" y="4.5" width="7.5" height="13.5" fill="white"/>
|
||||
<mask id="mask0_2028_5820" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="2" y="2" width="20" height="20">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.48 2 2 6.48 2 12C2 17.52 6.48 22 12 22C17.52 22 22 17.52 22 12C22 6.48 17.52 2 12 2ZM12 13C11.45 13 11 12.55 11 12V8C11 7.45 11.45 7 12 7C12.55 7 13 7.45 13 8V12C13 12.55 12.55 13 12 13ZM11 15V17H13V15H11Z" fill="black"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_2028_5820)">
|
||||
<rect width="24" height="24" fill="#0172CB"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 674 B |
@@ -46,8 +46,16 @@ class RouteName {
|
||||
static const String confirmPinScreen = 'confirmPinScreen';
|
||||
|
||||
//Forgot Password
|
||||
|
||||
static const String forgotPasswordPhoneVerificationScreen =
|
||||
'forgotPasswordPhoneVerificationScreen';
|
||||
static const String forgotPasswordScreen = 'forgotPasswordScreen';
|
||||
|
||||
//contact Admin
|
||||
static const String contactAdminScreen = 'contactAdminScreen';
|
||||
|
||||
//language change
|
||||
static const String languageChangeScreen = 'languageChangeScreen';
|
||||
|
||||
//delete Account
|
||||
static const String deleteAccountScreen = 'deleteAccountScreen';
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@ import 'package:tanami_app/features/MainScreens/Portfolio/presentation/pages/por
|
||||
import 'package:tanami_app/features/MainScreens/Wallet/presentation/pages/walletDetails.dart';
|
||||
|
||||
import 'package:tanami_app/features/biometric/presentation/pages/biometric_screen.dart';
|
||||
import 'package:tanami_app/features/contactAdmin/presentation/pages/contact_admin_screen.dart';
|
||||
|
||||
import 'package:tanami_app/features/countrySelection/presentation/pages/choose_country_screen.dart';
|
||||
import 'package:tanami_app/features/deleteAccount/presentation/pages/delete_account_screen.dart';
|
||||
import 'package:tanami_app/features/forgotPassword/presentation/pages/restore_password_screen.dart';
|
||||
import 'package:tanami_app/features/languageChange/presentation/pages/language_change_screen.dart';
|
||||
import 'package:tanami_app/features/otpVerification/presentation/pages/otp_screen.dart';
|
||||
import 'package:tanami_app/features/register/presentation/pages/register_screen.dart';
|
||||
import 'package:tanami_app/features/register/presentation/pages/register_step_screen.dart';
|
||||
@@ -154,12 +157,33 @@ final goRouter = GoRouter(
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
|
||||
name: RouteName.walletDetails,
|
||||
path: "${RouteName.walletDetails}/:type",
|
||||
builder: (context, state) {
|
||||
return WalletDetails(
|
||||
type: state.pathParameters["type"]!,
|
||||
);
|
||||
|
||||
name: RouteName.contactAdminScreen,
|
||||
path: RouteName.contactAdminScreen,
|
||||
builder: (context, state) {
|
||||
return const ContactAdminScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.languageChangeScreen,
|
||||
path: RouteName.languageChangeScreen,
|
||||
builder: (context, state) {
|
||||
return const LanguageChaneScreen();
|
||||
},
|
||||
),
|
||||
GoRoute(
|
||||
name: RouteName.deleteAccountScreen,
|
||||
path: RouteName.deleteAccountScreen,
|
||||
builder: (context, state) {
|
||||
return const DeleteAccountScreen();
|
||||
|
||||
},
|
||||
),
|
||||
],
|
||||
|
||||
@@ -73,4 +73,14 @@ class AppColor {
|
||||
//KYC Card Color
|
||||
static const Color kycCardTextColor = Color(0xFF074A23);
|
||||
static const Color kycCardBgColor = Color(0x7F074A23);
|
||||
|
||||
//Contact Admin Color
|
||||
static const Color contactAdminIconColor = Color(0xFF05391B);
|
||||
static const Color contactAdminBgColor = Color(0xFFE2EEE2);
|
||||
|
||||
//Language Color
|
||||
static const Color languageTextColor = Color(0xFF015698);
|
||||
|
||||
//Delete Account Color
|
||||
static const Color descriptionText = Color(0xFFC6C6C6);
|
||||
}
|
||||
|
||||
@@ -92,4 +92,14 @@ class AppImages {
|
||||
'assets/images/settings_screen/svg/reset_pin_icon.svg';
|
||||
static const String contactIcon =
|
||||
'assets/images/settings_screen/svg/contact_icon.svg';
|
||||
|
||||
//Contact
|
||||
static const String byPhoneIcon =
|
||||
'assets/images/contact_admin_screen/svg/phone_icon.svg';
|
||||
static const String byMailIcon =
|
||||
'assets/images/contact_admin_screen/svg/mail_icon.svg';
|
||||
|
||||
//Language
|
||||
static const String infoIcon =
|
||||
'assets/images/language_screen/png/info_icon.png';
|
||||
}
|
||||
|
||||
@@ -153,4 +153,32 @@ class AppText {
|
||||
static const String faqText = "FAQ";
|
||||
static const String logoutText = "Log Out";
|
||||
static const String deleteAccountText = "Delete account";
|
||||
|
||||
//Contact Admin
|
||||
static const String byPhoneText = "By phone";
|
||||
static const String byEmailText = "By-mail";
|
||||
static const String weAreHereToHelp = "We are here to help!";
|
||||
|
||||
//Language Screen
|
||||
static const String arabicText = "اللغة العربية";
|
||||
static const String chooseTheLanguageText = "Choose the language";
|
||||
static const String changingTheLanguageWillReloadApp =
|
||||
"Changing the language will reload the application";
|
||||
|
||||
//Delete Screen
|
||||
static const String weAreSadToSeeYouGo = "We're sad to see you go :(";
|
||||
static const String enterAdescription = "Enter a description...";
|
||||
static const String toHelpUsImprovePleaseLetusKnowWhyYouWantToDeleteAccount =
|
||||
"To help us improve, please let us know why you want to delete your account";
|
||||
static const String charactersLeft = "characters left";
|
||||
static const String iUnderstandAndAgreeThatmyDataWillBeDeleted =
|
||||
"I understand and agree that my data will be deleted";
|
||||
static const String noIWantToStay = "No, i want to stay";
|
||||
static const String pleaseEnteraDescription = "Please enter a description";
|
||||
static const String pleaseCheckThisField = "Please check this field";
|
||||
static const String weHaveReceivedYourRequestToDeleteAccount =
|
||||
"We have received your request to delete your account";
|
||||
static const String theRequestWillBeProcessed =
|
||||
"The request will be processed within 72 hours";
|
||||
static const closeText = "Close";
|
||||
}
|
||||
|
||||
19
lib/core/utils/url_launcher/url_launcher.dart
Normal file
19
lib/core/utils/url_launcher/url_launcher.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
void launchPhone(String phoneNumber) async {
|
||||
final url = 'tel:$phoneNumber';
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
await launchUrl(Uri.parse(url));
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
void launchEmail(String email) async {
|
||||
final url = 'mailto:$email';
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
await launchUrl(Uri.parse(url));
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/routes/route_name.dart';
|
||||
import 'package:tanami_app/core/routes/routes.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_images.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
@@ -37,7 +39,10 @@ class GeneralSettingsSection extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
const Gap(12),
|
||||
const SettingsListItem(
|
||||
SettingsListItem(
|
||||
onTapFunc: () {
|
||||
goRouter.pushNamed(RouteName.languageChangeScreen);
|
||||
},
|
||||
icon: AppImages.languageIcon,
|
||||
title: AppText.languageText,
|
||||
trailing: AppText.englishText,
|
||||
|
||||
@@ -37,13 +37,15 @@ class PrivacySettingsSection extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
const Gap(12),
|
||||
const SettingsListItem(
|
||||
SettingsListItem(
|
||||
onTapFunc: () {},
|
||||
icon: AppImages.resetPasswordIcon,
|
||||
title: AppText.resetPasswordText,
|
||||
trailing: "",
|
||||
),
|
||||
const Gap(8),
|
||||
const SettingsListItem(
|
||||
SettingsListItem(
|
||||
onTapFunc: () {},
|
||||
icon: AppImages.resetPinIcon,
|
||||
title: AppText.resetPinCodeText,
|
||||
trailing: "",
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/routes/route_name.dart';
|
||||
import 'package:tanami_app/core/routes/routes.dart';
|
||||
|
||||
import '../../../../../core/styles/app_color.dart';
|
||||
import '../../../../../core/styles/app_text.dart';
|
||||
@@ -24,14 +26,20 @@ class SettingsBottomSection extends StatelessWidget {
|
||||
height: 56.h,
|
||||
child: ButtonWidget().elevatedBtn(
|
||||
txtClr: AppColor.plainWhite,
|
||||
function: () {},
|
||||
function: () {
|
||||
goRouter.goNamed(RouteName.loginScreen, pathParameters: {
|
||||
"fromScreen": "registerStep",
|
||||
});
|
||||
},
|
||||
text: AppText.logoutText,
|
||||
clr: AppColor.primaryColor2,
|
||||
),
|
||||
),
|
||||
const Gap(5),
|
||||
ButtonWidget().textBtn(
|
||||
function: () {},
|
||||
function: () {
|
||||
goRouter.pushNamed(RouteName.deleteAccountScreen);
|
||||
},
|
||||
text: TextWidget().text14W700(
|
||||
AppText.deleteAccountText,
|
||||
clr: AppColor.hintTextColor,
|
||||
|
||||
@@ -8,12 +8,14 @@ class SettingsListItem extends StatelessWidget {
|
||||
final String icon;
|
||||
final String title;
|
||||
final String trailing;
|
||||
final Function() onTapFunc;
|
||||
|
||||
const SettingsListItem({
|
||||
super.key,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.trailing,
|
||||
required this.onTapFunc,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -27,6 +29,7 @@ class SettingsListItem extends StatelessWidget {
|
||||
color: AppColor.fillColor,
|
||||
),
|
||||
child: ListTile(
|
||||
onTap: onTapFunc,
|
||||
leading: SvgPicture.asset(icon),
|
||||
title: Text(title,
|
||||
style: GoogleFonts.dmSans(
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/routes/route_name.dart';
|
||||
import 'package:tanami_app/core/routes/routes.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_images.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
@@ -20,25 +22,31 @@ class SupportSettingsSection extends StatelessWidget {
|
||||
clr: AppColor.hintTextColor,
|
||||
),
|
||||
const Gap(8),
|
||||
const SettingsListItem(
|
||||
SettingsListItem(
|
||||
onTapFunc: () {
|
||||
goRouter.pushNamed(RouteName.contactAdminScreen);
|
||||
},
|
||||
icon: AppImages.contactIcon,
|
||||
title: AppText.contactAdminText,
|
||||
trailing: "",
|
||||
),
|
||||
const Gap(8),
|
||||
const SettingsListItem(
|
||||
SettingsListItem(
|
||||
onTapFunc: () {},
|
||||
icon: AppImages.privacyIcon,
|
||||
title: AppText.privacyPolicy,
|
||||
trailing: "",
|
||||
),
|
||||
const Gap(8),
|
||||
const SettingsListItem(
|
||||
SettingsListItem(
|
||||
onTapFunc: () {},
|
||||
icon: AppImages.privacyIcon,
|
||||
title: AppText.termsAndCondition,
|
||||
trailing: "",
|
||||
),
|
||||
const Gap(8),
|
||||
const SettingsListItem(
|
||||
SettingsListItem(
|
||||
onTapFunc: () {},
|
||||
icon: AppImages.faqIcon,
|
||||
title: AppText.faqText,
|
||||
trailing: "",
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tanami_app/features/contactAdmin/presentation/widgets/bottom_section.dart';
|
||||
|
||||
import '../../../../core/styles/app_text.dart';
|
||||
import '../../../../shared/components/appbar_widget.dart';
|
||||
import '../widgets/top_section.dart';
|
||||
|
||||
class ContactAdminScreen extends StatelessWidget {
|
||||
const ContactAdminScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const AppBarWidget(
|
||||
height: 75,
|
||||
titleTxt: AppText.contactAdminText,
|
||||
),
|
||||
body: ListView(
|
||||
children: [
|
||||
topSection(),
|
||||
bottomSection(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_images.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
import 'package:tanami_app/core/utils/url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../../shared/components/text_widget.dart';
|
||||
|
||||
Widget bottomSection() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(22.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const Gap(50.0),
|
||||
contactOption(
|
||||
icon: AppImages.byPhoneIcon,
|
||||
title: AppText.byPhoneText,
|
||||
subtitle: '+973 12345678',
|
||||
onTap: () {
|
||||
launchPhone('+973 12345678');
|
||||
},
|
||||
),
|
||||
const Gap(16.0),
|
||||
contactOption(
|
||||
icon: AppImages.byMailIcon,
|
||||
title: AppText.byEmailText,
|
||||
subtitle: 'info@tanamicapital.com',
|
||||
onTap: () {
|
||||
launchEmail('info@tanamicapital.com');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget contactOption({
|
||||
required String icon,
|
||||
required String title,
|
||||
required String subtitle,
|
||||
required VoidCallback onTap,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.contactAdminBgColor,
|
||||
borderRadius: BorderRadius.circular(22.0),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
icon,
|
||||
),
|
||||
const Gap(16.0),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextWidget().text14W600(
|
||||
title,
|
||||
clr: AppColor.contactAdminIconColor,
|
||||
),
|
||||
TextWidget().text14W400(
|
||||
subtitle,
|
||||
clr: AppColor.contactAdminIconColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
const Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
color: AppColor.contactAdminIconColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/svg.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
|
||||
import '../../../../core/styles/app_color.dart';
|
||||
import '../../../../core/styles/app_images.dart';
|
||||
import '../../../../core/styles/app_text.dart';
|
||||
import '../../../../shared/components/text_widget.dart';
|
||||
|
||||
Widget topSection() {
|
||||
return Column(
|
||||
children: [
|
||||
const Gap(85),
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
AppImages.weclomeLogo,
|
||||
),
|
||||
),
|
||||
const Gap(24),
|
||||
TextWidget().text17W700(
|
||||
AppText.weAreHereToHelp,
|
||||
clr: AppColor.textLabelColor,
|
||||
),
|
||||
const Gap(10),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tanami_app/features/deleteAccount/presentation/bloc/delete_account_event.dart';
|
||||
import 'package:tanami_app/features/deleteAccount/presentation/bloc/delete_account_state.dart';
|
||||
|
||||
class DeleteAccountBloc extends Bloc<DeleteAccountEvent, DeleteAccountState> {
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
final TextEditingController descriptionTextField = TextEditingController();
|
||||
|
||||
GlobalKey<FormState> getFormKey() {
|
||||
return formKey;
|
||||
}
|
||||
|
||||
DeleteAccountBloc() : super(DeleteAccountInitial()) {
|
||||
descriptionTextField.addListener(_onFormFieldChanged);
|
||||
|
||||
on<DeleteAccountFormChanged>(_onLoginFormChanged);
|
||||
on<DeleteAccountSubmitted>((event, emit) async {
|
||||
if (!formKey.currentState!.validate()) {
|
||||
return;
|
||||
}
|
||||
emit(DeleteAccountLoading());
|
||||
try {
|
||||
// Simulate API call
|
||||
await Future.delayed(const Duration(seconds: 2));
|
||||
// Replace the next line with actual API call
|
||||
final isSuccess = _mockApiCheck();
|
||||
if (isSuccess) {
|
||||
emit(DeleteAccountSuccess());
|
||||
} else {
|
||||
emit(const DeleteAccountFailure("Failed."));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(DeleteAccountFailure(e.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool _mockApiCheck() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void _onFormFieldChanged() {
|
||||
add(DeleteAccountFormChanged(
|
||||
descriptionTextField.text,
|
||||
));
|
||||
}
|
||||
|
||||
void _onLoginFormChanged(
|
||||
DeleteAccountFormChanged event, Emitter<DeleteAccountState> emit) {
|
||||
final areFieldsFilled = event.description.isNotEmpty;
|
||||
emit(DeleteAccountFieldsState(areFieldsFilled));
|
||||
}
|
||||
|
||||
// Method to reset text fields
|
||||
void resetFields() {
|
||||
descriptionTextField.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
descriptionTextField.dispose();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class DeleteAccountEvent extends Equatable {
|
||||
const DeleteAccountEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class DeleteAccountSubmitted extends DeleteAccountEvent {
|
||||
final String description;
|
||||
|
||||
const DeleteAccountSubmitted(
|
||||
this.description,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object> get props => [description];
|
||||
}
|
||||
|
||||
class DeleteAccountFormChanged extends DeleteAccountEvent {
|
||||
final String description;
|
||||
|
||||
const DeleteAccountFormChanged(
|
||||
this.description,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object> get props => [description];
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class DeleteAccountState extends Equatable {
|
||||
const DeleteAccountState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class DeleteAccountInitial extends DeleteAccountState {}
|
||||
|
||||
class DeleteAccountLoading extends DeleteAccountState {}
|
||||
|
||||
class DeleteAccountSuccess extends DeleteAccountState {}
|
||||
|
||||
class DeleteAccountFailure extends DeleteAccountState {
|
||||
final String error;
|
||||
|
||||
const DeleteAccountFailure(this.error);
|
||||
|
||||
@override
|
||||
List<Object> get props => [error];
|
||||
}
|
||||
|
||||
class DeleteAccountFieldsState extends DeleteAccountState {
|
||||
final bool areFieldsFilled;
|
||||
|
||||
const DeleteAccountFieldsState(this.areFieldsFilled);
|
||||
|
||||
@override
|
||||
List<Object> get props => [areFieldsFilled];
|
||||
}
|
||||
16
lib/features/deleteAccount/presentation/bloc/text_bloc.dart
Normal file
16
lib/features/deleteAccount/presentation/bloc/text_bloc.dart
Normal file
@@ -0,0 +1,16 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'text_event.dart';
|
||||
import 'text_state.dart';
|
||||
|
||||
class TextBloc extends Bloc<TextEvent, TextState> {
|
||||
final int maxCharacters;
|
||||
|
||||
TextBloc(this.maxCharacters) : super(TextInitial()) {
|
||||
on<TextChanged>(_onTextChanged);
|
||||
}
|
||||
|
||||
void _onTextChanged(TextChanged event, Emitter<TextState> emit) {
|
||||
final charactersLeft = maxCharacters - event.text.length;
|
||||
emit(TextUpdated(event.text, charactersLeft));
|
||||
}
|
||||
}
|
||||
15
lib/features/deleteAccount/presentation/bloc/text_event.dart
Normal file
15
lib/features/deleteAccount/presentation/bloc/text_event.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class TextEvent extends Equatable {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class TextChanged extends TextEvent {
|
||||
final String text;
|
||||
|
||||
TextChanged(this.text);
|
||||
|
||||
@override
|
||||
List<Object> get props => [text];
|
||||
}
|
||||
18
lib/features/deleteAccount/presentation/bloc/text_state.dart
Normal file
18
lib/features/deleteAccount/presentation/bloc/text_state.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class TextState extends Equatable {
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class TextInitial extends TextState {}
|
||||
|
||||
class TextUpdated extends TextState {
|
||||
final String text;
|
||||
final int charactersLeft;
|
||||
|
||||
TextUpdated(this.text, this.charactersLeft);
|
||||
|
||||
@override
|
||||
List<Object> get props => [text, charactersLeft];
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/features/deleteAccount/presentation/widgets/top_section.dart';
|
||||
|
||||
import '../widgets/bottom_section.dart';
|
||||
import '../widgets/form_section.dart';
|
||||
|
||||
class DeleteAccountLayout extends StatelessWidget {
|
||||
const DeleteAccountLayout({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
const TopSection(),
|
||||
const FormSection(),
|
||||
const Gap(30),
|
||||
bottomSection(context),
|
||||
],
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tanami_app/features/deleteAccount/presentation/bloc/text_bloc.dart';
|
||||
import 'package:tanami_app/features/deleteAccount/presentation/pages/delete_account_layout.dart';
|
||||
|
||||
import '../../../../core/styles/app_text.dart';
|
||||
import '../../../../shared/components/appbar_widget.dart';
|
||||
import '../../../../shared/components/bloc/checkbox/checkbox_bloc.dart';
|
||||
import '../bloc/delete_account_bloc.dart';
|
||||
|
||||
class DeleteAccountScreen extends StatelessWidget {
|
||||
const DeleteAccountScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: const AppBarWidget(
|
||||
height: 75,
|
||||
titleTxt: AppText.deleteAccountText,
|
||||
),
|
||||
body: MultiBlocProvider(
|
||||
providers: [
|
||||
BlocProvider(
|
||||
// Create an instance of the OnboardingBloc
|
||||
create: (context) => DeleteAccountBloc(),
|
||||
),
|
||||
BlocProvider(
|
||||
// Create an instance of the OnboardingBloc
|
||||
create: (context) => CheckboxBloc(),
|
||||
),
|
||||
BlocProvider(
|
||||
// Create an instance of the OnboardingBloc
|
||||
create: (context) => TextBloc(350),
|
||||
),
|
||||
],
|
||||
child: const DeleteAccountLayout(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
|
||||
import '../../../../core/routes/routes.dart';
|
||||
import '../../../../core/styles/app_color.dart';
|
||||
import '../../../../core/styles/app_text.dart';
|
||||
import '../../../../shared/components/bloc/checkbox/checkbox_bloc.dart';
|
||||
import '../../../../shared/components/bloc/checkbox/checkbox_event.dart';
|
||||
import '../../../../shared/components/bloc/checkbox/checkbox_state.dart';
|
||||
import '../../../../shared/components/button_widget.dart';
|
||||
import '../../../../shared/components/loader.dart';
|
||||
import '../../../../shared/components/text_widget.dart';
|
||||
import '../../../../shared/components/toast_message.dart';
|
||||
import '../bloc/delete_account_bloc.dart';
|
||||
import '../bloc/delete_account_event.dart';
|
||||
import '../bloc/delete_account_state.dart';
|
||||
|
||||
Widget bottomSection(BuildContext context) {
|
||||
final deleteAccountBloc = context.read<DeleteAccountBloc>();
|
||||
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 10,
|
||||
),
|
||||
width: 1.sw,
|
||||
height: 56.h,
|
||||
child: ButtonWidget().elevatedBtn(
|
||||
txtClr: AppColor.plainWhite,
|
||||
function: () {
|
||||
goRouter.pop();
|
||||
},
|
||||
text: AppText.noIWantToStay,
|
||||
clr: AppColor.primaryColor2,
|
||||
),
|
||||
),
|
||||
BlocConsumer<DeleteAccountBloc, DeleteAccountState>(
|
||||
listener: (context, state) {
|
||||
if (state is DeleteAccountLoading) {
|
||||
Loader.loader(context);
|
||||
} else if (state is DeleteAccountSuccess) {
|
||||
goRouter.pop();
|
||||
} else if (state is DeleteAccountFailure) {
|
||||
goRouter.pop();
|
||||
errorToastMessage(
|
||||
context,
|
||||
state.error,
|
||||
);
|
||||
}
|
||||
},
|
||||
builder: (context, state) {
|
||||
return ButtonWidget().textBtn(
|
||||
function: () {
|
||||
if (deleteAccountBloc.formKey.currentState!.validate()) {
|
||||
context.read<CheckboxBloc>().add(ValidateCheckbox());
|
||||
if (context.read<CheckboxBloc>().state is CheckboxChecked) {
|
||||
context.read<DeleteAccountBloc>().add(
|
||||
DeleteAccountSubmitted(
|
||||
context
|
||||
.read<DeleteAccountBloc>()
|
||||
.descriptionTextField
|
||||
.text,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
text: TextWidget().text14W700(AppText.deleteAccountText,
|
||||
clr: AppColor.hintTextColor,
|
||||
textDecoration: TextDecoration.underline));
|
||||
},
|
||||
),
|
||||
const Gap(20),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
import 'package:tanami_app/shared/components/text_widget.dart';
|
||||
|
||||
import '../../../../shared/components/bloc/checkbox/checkbox_bloc.dart';
|
||||
import '../../../../shared/components/bloc/checkbox/checkbox_state.dart';
|
||||
import '../../../../shared/components/checkbox_widget.dart';
|
||||
import '../../../../shared/components/form_label_textfield.dart';
|
||||
import '../bloc/delete_account_bloc.dart';
|
||||
import '../bloc/text_bloc.dart';
|
||||
import '../bloc/text_event.dart';
|
||||
import '../bloc/text_state.dart';
|
||||
|
||||
class FormSection extends StatelessWidget {
|
||||
const FormSection({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final deleteAccountBloc = context.read<DeleteAccountBloc>();
|
||||
return Form(
|
||||
key: deleteAccountBloc.formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 16.0,
|
||||
right: 16,
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Gap(40),
|
||||
BlocBuilder<TextBloc, TextState>(
|
||||
builder: (context, state) {
|
||||
return FormLabelTextField(
|
||||
onChangeFun: (p0) {
|
||||
context.read<TextBloc>().add(TextChanged(
|
||||
deleteAccountBloc.descriptionTextField.text,
|
||||
));
|
||||
},
|
||||
hintText: AppText.enterAdescription,
|
||||
textEditingController:
|
||||
deleteAccountBloc.descriptionTextField,
|
||||
title: AppText
|
||||
.toHelpUsImprovePleaseLetusKnowWhyYouWantToDeleteAccount,
|
||||
type: "description",
|
||||
);
|
||||
},
|
||||
),
|
||||
const Gap(8),
|
||||
BlocBuilder<TextBloc, TextState>(
|
||||
builder: (context, state) {
|
||||
if (state is TextUpdated) {
|
||||
return TextWidget().text14W400(
|
||||
"${state.charactersLeft} ${AppText.charactersLeft}",
|
||||
clr: AppColor.descriptionText);
|
||||
}
|
||||
return const Text('350 characters left');
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 1.sw,
|
||||
height: 70.h,
|
||||
),
|
||||
const CheckBoxWidget(),
|
||||
Positioned(
|
||||
left: 45,
|
||||
top: 14,
|
||||
child: SizedBox(
|
||||
width: 0.75.sw,
|
||||
child: TextWidget().text14W500(
|
||||
AppText.iUnderstandAndAgreeThatmyDataWillBeDeleted,
|
||||
clr: AppColor.charcoalColor,
|
||||
txtAlign: TextAlign.start,
|
||||
),
|
||||
),
|
||||
),
|
||||
BlocBuilder<CheckboxBloc, CheckBoxState>(
|
||||
builder: (context, state) {
|
||||
if (state is CheckboxError) {
|
||||
return Positioned(
|
||||
left: 45,
|
||||
top: 55,
|
||||
child: SizedBox(
|
||||
width: 0.75.sw,
|
||||
child: TextWidget().text14W500(
|
||||
AppText.pleaseCheckThisField,
|
||||
clr: AppColor.txtErrorColor,
|
||||
txtAlign: TextAlign.start,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_images.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
import 'package:tanami_app/shared/components/text_widget.dart';
|
||||
|
||||
class TopSection extends StatelessWidget {
|
||||
const TopSection({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
const Gap(85),
|
||||
Center(
|
||||
child: SvgPicture.asset(
|
||||
AppImages.weclomeLogo,
|
||||
),
|
||||
),
|
||||
const Gap(30),
|
||||
TextWidget().text20W700(
|
||||
AppText.weAreSadToSeeYouGo,
|
||||
clr: AppColor.charcoalColor,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import 'choose_language_event.dart';
|
||||
import 'choose_language_state.dart';
|
||||
|
||||
class ChooseLanguageBloc
|
||||
extends Bloc<ChooseLanguageEvent, ChooseLanguageState> {
|
||||
ChooseLanguageBloc() : super(ChooseLanguageInitial()) {
|
||||
on<ChooseLanguageSelected>(_onRadioSelected);
|
||||
on<ResetRadioSelection>(_onResetRadioSelection);
|
||||
}
|
||||
|
||||
void _onRadioSelected(
|
||||
ChooseLanguageSelected event, Emitter<ChooseLanguageState> emit) {
|
||||
emit(ChooseLanguageSelectionChanged(event.selectedIndex));
|
||||
}
|
||||
|
||||
void _onResetRadioSelection(
|
||||
ResetRadioSelection event, Emitter<ChooseLanguageState> emit) {
|
||||
emit(ChooseLanguageInitial());
|
||||
}
|
||||
|
||||
void resetSelection() {
|
||||
add(ResetRadioSelection());
|
||||
}
|
||||
|
||||
int get selectedCountry {
|
||||
if (state is ChooseLanguageSelectionChanged) {
|
||||
return (state as ChooseLanguageSelectionChanged).selectedIndex;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class ChooseLanguageEvent extends Equatable {
|
||||
const ChooseLanguageEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class ChooseLanguageSelected extends ChooseLanguageEvent {
|
||||
final int selectedIndex;
|
||||
|
||||
const ChooseLanguageSelected([this.selectedIndex = 0]);
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedIndex];
|
||||
}
|
||||
|
||||
class ResetRadioSelection extends ChooseLanguageEvent {}
|
||||
@@ -0,0 +1,19 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class ChooseLanguageState extends Equatable {
|
||||
const ChooseLanguageState();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class ChooseLanguageInitial extends ChooseLanguageState {}
|
||||
|
||||
class ChooseLanguageSelectionChanged extends ChooseLanguageState {
|
||||
final int selectedIndex;
|
||||
|
||||
const ChooseLanguageSelectionChanged(this.selectedIndex);
|
||||
|
||||
@override
|
||||
List<Object> get props => [selectedIndex];
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_images.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
import 'package:tanami_app/shared/components/appbar_widget.dart';
|
||||
import 'package:tanami_app/shared/components/text_widget.dart';
|
||||
import '../widgets/bottom_section.dart';
|
||||
import '../widgets/language_change_list.dart';
|
||||
|
||||
class LanguageChangeLayout extends StatelessWidget {
|
||||
const LanguageChangeLayout({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
bottomNavigationBar: bottomSection(),
|
||||
appBar: const AppBarWidget(
|
||||
height: 75,
|
||||
titleTxt: AppText.languageText,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
children: [
|
||||
const CountrySelectionList(),
|
||||
const Gap(24),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Image.asset(
|
||||
width: 24,
|
||||
height: 24,
|
||||
AppImages.infoIcon,
|
||||
),
|
||||
const Gap(8),
|
||||
SizedBox(
|
||||
width: 0.8.sw,
|
||||
child: TextWidget().text12W500(
|
||||
AppText.changingTheLanguageWillReloadApp,
|
||||
clr: AppColor.languageTextColor,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tanami_app/features/languageChange/presentation/bloc/choose_language_bloc.dart';
|
||||
|
||||
import 'language_change_layout.dart';
|
||||
|
||||
class LanguageChaneScreen extends StatelessWidget {
|
||||
const LanguageChaneScreen({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
resizeToAvoidBottomInset: true,
|
||||
body: MultiBlocProvider(
|
||||
// Define the providers for the OnboardingBloc and other blocs
|
||||
providers: [
|
||||
BlocProvider(
|
||||
// Create an instance of the OnboardingBloc
|
||||
create: (context) => ChooseLanguageBloc(),
|
||||
),
|
||||
],
|
||||
child: const LanguageChangeLayout(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
|
||||
import '../../../../core/routes/routes.dart';
|
||||
import '../../../../core/styles/app_color.dart';
|
||||
import '../../../../core/styles/app_text.dart';
|
||||
import '../../../../shared/components/button_widget.dart';
|
||||
import '../../../../shared/components/text_widget.dart';
|
||||
|
||||
Widget bottomSection() {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 10,
|
||||
),
|
||||
width: 1.sw,
|
||||
height: 56.h,
|
||||
child: ButtonWidget().elevatedBtn(
|
||||
txtClr: AppColor.plainWhite,
|
||||
function: () {
|
||||
goRouter.pop();
|
||||
},
|
||||
text: AppText.submitText,
|
||||
clr: AppColor.primaryColor2,
|
||||
),
|
||||
),
|
||||
ButtonWidget().textBtn(
|
||||
function: () {
|
||||
goRouter.pop();
|
||||
},
|
||||
text: TextWidget().text14W700(AppText.backText,
|
||||
clr: AppColor.textLabelColor,
|
||||
textDecoration: TextDecoration.underline)),
|
||||
const Gap(20),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
import 'package:tanami_app/shared/components/text_widget.dart';
|
||||
|
||||
import '../bloc/choose_language_bloc.dart';
|
||||
import '../bloc/choose_language_event.dart';
|
||||
import '../bloc/choose_language_state.dart';
|
||||
|
||||
class CountrySelectionList extends StatelessWidget {
|
||||
const CountrySelectionList({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final radioBloc = context.read<ChooseLanguageBloc>();
|
||||
return BlocBuilder<ChooseLanguageBloc, ChooseLanguageState>(
|
||||
builder: (context, state) {
|
||||
int selectedIndex = 0;
|
||||
if (state is ChooseLanguageSelectionChanged) {
|
||||
selectedIndex = state.selectedIndex;
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: List<Widget>.generate(languageList.length, (int index) {
|
||||
return Row(
|
||||
children: [
|
||||
Radio<int>(
|
||||
activeColor: AppColor.radioActiveColor,
|
||||
value: index,
|
||||
groupValue: selectedIndex,
|
||||
onChanged: (int? value) {
|
||||
if (value != null) {
|
||||
radioBloc.add(ChooseLanguageSelected(value));
|
||||
}
|
||||
},
|
||||
),
|
||||
TextWidget().text14W500(languageList[index],
|
||||
clr: AppColor.charcoalColor),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> languageList = [
|
||||
AppText.englishText,
|
||||
AppText.arabicText,
|
||||
];
|
||||
@@ -21,6 +21,8 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
|
||||
on<LoginFormChanged>(_onLoginFormChanged);
|
||||
on<LoginSubmitted>((event, emit) async {
|
||||
if (!formKey.currentState!.validate()) {
|
||||
emit(
|
||||
const LoginFailure("Login failed. Please check your credentials."));
|
||||
return;
|
||||
}
|
||||
emit(LoginLoading());
|
||||
|
||||
@@ -15,7 +15,7 @@ class LoginSignUpButton extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 40.0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 20.0),
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
|
||||
import 'checkbox_event.dart';
|
||||
import 'checkbox_state.dart';
|
||||
@@ -6,6 +7,7 @@ import 'checkbox_state.dart';
|
||||
class CheckboxBloc extends Bloc<CheckboxEvent, CheckBoxState> {
|
||||
CheckboxBloc() : super(CheckboxUnchecked()) {
|
||||
on<ToggleCheckbox>(_onToggleCheckbox);
|
||||
on<ValidateCheckbox>(_onValidateCheckbox);
|
||||
}
|
||||
|
||||
void _onToggleCheckbox(ToggleCheckbox event, Emitter<CheckBoxState> emit) {
|
||||
@@ -15,4 +17,11 @@ class CheckboxBloc extends Bloc<CheckboxEvent, CheckBoxState> {
|
||||
emit(CheckboxUnchecked());
|
||||
}
|
||||
}
|
||||
|
||||
void _onValidateCheckbox(
|
||||
ValidateCheckbox event, Emitter<CheckBoxState> emit) {
|
||||
if (state is! CheckboxChecked) {
|
||||
emit(const CheckboxError(AppText.pleaseCheckThisField));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,3 +8,5 @@ abstract class CheckboxEvent extends Equatable {
|
||||
}
|
||||
|
||||
class ToggleCheckbox extends CheckboxEvent {}
|
||||
|
||||
class ValidateCheckbox extends CheckboxEvent {}
|
||||
|
||||
@@ -10,3 +10,12 @@ abstract class CheckBoxState extends Equatable {
|
||||
class CheckboxUnchecked extends CheckBoxState {}
|
||||
|
||||
class CheckboxChecked extends CheckBoxState {}
|
||||
|
||||
class CheckboxError extends CheckBoxState {
|
||||
final String message;
|
||||
|
||||
const CheckboxError(this.message);
|
||||
|
||||
@override
|
||||
List<Object> get props => [message];
|
||||
}
|
||||
|
||||
@@ -1,16 +1,32 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/shared/components/text_widget.dart';
|
||||
|
||||
class ButtonWidget {
|
||||
//Text Button
|
||||
Widget textBtn({
|
||||
required Widget text,
|
||||
Color? clr,
|
||||
required VoidCallback function,
|
||||
}) {
|
||||
return TextButton(
|
||||
onPressed: function,
|
||||
child: text,
|
||||
return InkWell(
|
||||
onTap: function,
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: ShapeDecoration(
|
||||
shape: RoundedRectangleBorder(
|
||||
side: const BorderSide(width: 1, color: AppColor.txtBorderColor),
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
),
|
||||
),
|
||||
margin: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 10,
|
||||
),
|
||||
width: 1.sw,
|
||||
height: 56.h,
|
||||
child: Center(child: text),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:tanami_app/core/styles/app_color.dart';
|
||||
import 'package:tanami_app/core/styles/app_text.dart';
|
||||
@@ -17,12 +18,14 @@ class FormLabelTextField extends StatelessWidget {
|
||||
required this.textEditingController,
|
||||
required this.hintText,
|
||||
this.prefixWidget,
|
||||
this.onChangeFun,
|
||||
});
|
||||
final String title;
|
||||
final String type;
|
||||
final String hintText;
|
||||
final TextEditingController textEditingController;
|
||||
final Widget? prefixWidget;
|
||||
final Function(String)? onChangeFun;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -32,6 +35,7 @@ class FormLabelTextField extends StatelessWidget {
|
||||
TextWidget().text14W500(
|
||||
title,
|
||||
clr: AppColor.textLabelColor,
|
||||
txtAlign: type == "description" ? TextAlign.start : TextAlign.center,
|
||||
),
|
||||
const Gap(10),
|
||||
type == "password"
|
||||
@@ -39,6 +43,7 @@ class FormLabelTextField extends StatelessWidget {
|
||||
controller: textEditingController,
|
||||
)
|
||||
: textFormField(
|
||||
onInput: onChangeFun,
|
||||
validator: (value) {
|
||||
if (type == "phone number") {
|
||||
if (value != null && value.isEmpty) {
|
||||
@@ -50,10 +55,19 @@ class FormLabelTextField extends StatelessWidget {
|
||||
return AppText.chooseCountry;
|
||||
}
|
||||
return null;
|
||||
} else if (type == "description") {
|
||||
if (textEditingController.text.isEmpty) {
|
||||
return AppText.pleaseEnteraDescription;
|
||||
}
|
||||
return null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(350),
|
||||
],
|
||||
maxlines: type == "description" ? 6 : 1,
|
||||
texttype: type == "phone number"
|
||||
? TextInputType.phone
|
||||
: TextInputType.name,
|
||||
|
||||
@@ -24,13 +24,11 @@ Widget textFormField({
|
||||
return TextFormField(
|
||||
validator: validator,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
// cursorColor: AppColor.txtBorderShadowColor,
|
||||
cursorColor: AppColor.primaryColor2,
|
||||
initialValue: value,
|
||||
readOnly: readonly!,
|
||||
onTap: onTap,
|
||||
|
||||
enabled: enabled,
|
||||
enableInteractiveSelection: false,
|
||||
maxLines: maxlines,
|
||||
autovalidateMode: AutovalidateMode.onUserInteraction,
|
||||
controller: textEditingController,
|
||||
|
||||
@@ -1222,13 +1222,13 @@ packages:
|
||||
source: hosted
|
||||
version: "10.3.0"
|
||||
url_launcher:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
|
||||
sha256: "21b704ce5fa560ea9f3b525b43601c678728ba46725bab9b01187b4831377ed3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.6"
|
||||
version: "6.3.0"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -78,6 +78,9 @@ dependencies:
|
||||
sms_autofill: ^2.3.1
|
||||
carousel_slider: ^4.2.1
|
||||
|
||||
#Url Launcher
|
||||
url_launcher: ^6.3.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
@@ -109,5 +112,9 @@ flutter:
|
||||
- assets/images/biometric_screen/svg/
|
||||
- assets/images/dialog/
|
||||
- assets/images/dialog/svg/
|
||||
- assets/images/contact_admin_screen/
|
||||
- assets/images/contact_admin_screen/svg/
|
||||
- assets/images/settings_screen/
|
||||
- assets/images/settings_screen/svg/
|
||||
- assets/images/language_screen/
|
||||
- assets/images/language_screen/png/
|
||||
|
||||
Reference in New Issue
Block a user