diff --git a/assets/images/invest_screen/svg/scanner.svg b/assets/images/invest_screen/svg/scanner.svg new file mode 100644 index 0000000..e8062fd --- /dev/null +++ b/assets/images/invest_screen/svg/scanner.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/assets/images/invest_screen/svg/shield_done.svg b/assets/images/invest_screen/svg/shield_done.svg new file mode 100644 index 0000000..b717469 --- /dev/null +++ b/assets/images/invest_screen/svg/shield_done.svg @@ -0,0 +1,3 @@ + + + diff --git a/assets/images/invest_screen/svg/ticket.svg b/assets/images/invest_screen/svg/ticket.svg new file mode 100644 index 0000000..9d29f0c --- /dev/null +++ b/assets/images/invest_screen/svg/ticket.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/assets/images/invest_screen/svg/time_square.svg b/assets/images/invest_screen/svg/time_square.svg new file mode 100644 index 0000000..595e77c --- /dev/null +++ b/assets/images/invest_screen/svg/time_square.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/images/invest_screen/svg/wallet.svg b/assets/images/invest_screen/svg/wallet.svg new file mode 100644 index 0000000..003542b --- /dev/null +++ b/assets/images/invest_screen/svg/wallet.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/core/routes/route_name.dart b/lib/core/routes/route_name.dart index 13a7870..2c02344 100644 --- a/lib/core/routes/route_name.dart +++ b/lib/core/routes/route_name.dart @@ -64,4 +64,7 @@ class RouteName { //change password static const String changePasswordScreen = "changePasswordScreen"; + + //invest + static const String investDetailScreen = "investDetailScreen"; } diff --git a/lib/core/routes/routes.dart b/lib/core/routes/routes.dart index 3625a83..490fcc2 100644 --- a/lib/core/routes/routes.dart +++ b/lib/core/routes/routes.dart @@ -3,6 +3,7 @@ import 'package:go_router/go_router.dart'; import 'package:tanami_app/core/routes/route_name.dart'; import 'package:tanami_app/features/MainScreens/Academy/presentation/pages/academy_details_screen.dart'; +import 'package:tanami_app/features/MainScreens/Invest/presentation/pages/invest_details_screen.dart'; import 'package:tanami_app/features/MainScreens/Portfolio/presentation/pages/portfolio_details_screen.dart'; import 'package:tanami_app/features/MainScreens/Wallet/presentation/pages/walletDetails.dart'; @@ -217,6 +218,13 @@ final goRouter = GoRouter( return const ChangePasswordScreen(); }, ), + GoRoute( + name: RouteName.investDetailScreen, + path: RouteName.investDetailScreen, + builder: (context, state) { + return const InvestDetailsScreen(); + }, + ), ], ), ], diff --git a/lib/core/styles/app_color.dart b/lib/core/styles/app_color.dart index 8a54af0..c75199a 100644 --- a/lib/core/styles/app_color.dart +++ b/lib/core/styles/app_color.dart @@ -83,4 +83,11 @@ class AppColor { //Delete Account Color static const Color descriptionText = Color(0xFFC6C6C6); + + //Invest Color + static const Color investKycCardColor = Color(0xFFEEF5FA); + static const Color investKycBorderColor = Color(0xFFB0D3EF); + static const Color investKycBoxShadow1Color = Color(0xFF90D4FF); + static const Color investKycBoxShadow2Color = Color(0xA0DAF0FF); + static const Color investTextColor = Color(0xFF066123); } diff --git a/lib/core/styles/app_images.dart b/lib/core/styles/app_images.dart index 4099df1..4e8f49c 100644 --- a/lib/core/styles/app_images.dart +++ b/lib/core/styles/app_images.dart @@ -102,4 +102,14 @@ class AppImages { //Language static const String infoIcon = 'assets/images/language_screen/png/info_icon.png'; + + //Invest + static const String investIcon = + 'assets/images/invest_screen/svg/shield_done.svg'; + static const String scannerIcon = + 'assets/images/invest_screen/svg/scanner.svg'; + static const String ticketIcon = 'assets/images/invest_screen/svg/ticket.svg'; + static const String walletIcon = 'assets/images/invest_screen/svg/wallet.svg'; + static const String timeSquareIcon = + 'assets/images/invest_screen/svg/time_square.svg'; } diff --git a/lib/core/styles/app_text.dart b/lib/core/styles/app_text.dart index 9ed6ab8..8ffa8ec 100644 --- a/lib/core/styles/app_text.dart +++ b/lib/core/styles/app_text.dart @@ -121,7 +121,6 @@ class AppText { static const String declineText = "Decline"; //Forgot Password - static const String restorePasswordText = "Restore password"; static const String toRestorePasswordPleaseEnterPhoneNumber = "To restore password please enter phone number"; @@ -132,6 +131,30 @@ class AppText { static const String walletTitle = "Wallet balance"; static const String day = "Today"; static const String onHold = "On hold"; + static const String walletDetailsTitle = "Details"; + static const String withdrawalScreenTitle = 'Enter bank details'; + static const String status = "Status"; + static const String paymentMethod = "Payment Method"; + static const String accountName = "Account Name"; + static const String accountHolderName = + 'Account Holder Name/Beneficiary Name'; + static const String iban = "IBAN"; + static const String beneficiaryAddress = "Beneficiary Address"; + static const String bankName = "Bank Name"; + static const String branchAddress = "Branch Address"; + static const String SWIFTcode = "SWIFT/BIC code"; + static const String refid = "Reference ID"; + static const String previewTitle = "Withdrawal confirmation"; + static const String info1 = + 'Please confirm the withdrawal amount and verify the accuracy of your bank details.'; + static const String balance = 'Wallet balance: '; + static const String withdrawTitle = 'Withdrawal'; + static const String withdrawDetails = 'Withdrawal details'; + static const String required = 'Fields with * are required'; + static const String withdrawAmt = 'Total Withdrawal amount:'; + static const String info2 = 'Payment can include transfer fee from your bank'; + static const String next = 'Next'; + static const String submit = 'Submit request'; //Settings static const String settingsText = "Settings"; @@ -158,6 +181,10 @@ class AppText { static const String noText = "No"; static const String yesText = "Yes"; static const String pinUpdatedSucess = "Pin updated Sucessfully !"; + static const String passwordUpdatedSucess = "Password updated Sucessfully !"; + static const String changePasswordText = "Change Password"; + static const String newPasswordText = "New Password"; + static const String currentPsswordText = "Current Password"; //Contact Admin static const String byPhoneText = "By phone"; @@ -186,4 +213,19 @@ class AppText { static const String theRequestWillBeProcessed = "The request will be processed within 72 hours"; static const closeText = "Close"; + + //Invest + static const String investText = "Invest"; + static const String availableText = "Available"; + static const String closedText = "Closed"; + static const String completeYourVerification = "Complete your verification"; + static const String verifyYourAccountInUnderMinutesToStartInvestingToday = + "Verify your account in under 10 minutes to start investing today!"; + static const String closingDateText = "Closing Date"; + static const String fundedText = "funded"; + static const String sponsorNameText = "Sponsor name"; + static const String estimatedReturnText = "Estimated return"; + static const String holdingPeriodText = "Holding period"; + static const String minimumInvestmentText = "Minimum investment"; + static const String keyMeritsIOfnvestmentText = "Key Merits of Investment"; } diff --git a/lib/features/MainScreens/Invest/presentation/bloc/tab_bloc.dart b/lib/features/MainScreens/Invest/presentation/bloc/tab_bloc.dart new file mode 100644 index 0000000..a953b48 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/bloc/tab_bloc.dart @@ -0,0 +1,25 @@ +import 'package:bloc/bloc.dart'; + +import 'tab_event.dart'; +import 'tab_state.dart'; + +class TabBloc extends Bloc { + TabBloc() : super(const TabState()) { + on(_onLoadAvailableItems); + on(_onLoadClosedItems); + } + + void _onLoadAvailableItems(LoadAvailableItems event, Emitter emit) { + // Simulate fetching available items + final availableItems = + List.generate(10, (index) => 'Available Item $index'); + emit(state.copyWith(availableItems: availableItems)); + } + + void _onLoadClosedItems(LoadClosedItems event, Emitter emit) { + // Simulate fetching closed items + final closedItems = + List.generate(10, (index) => 'Closed Item $index'); + emit(state.copyWith(closedItems: closedItems)); + } +} diff --git a/lib/features/MainScreens/Invest/presentation/bloc/tab_event.dart b/lib/features/MainScreens/Invest/presentation/bloc/tab_event.dart new file mode 100644 index 0000000..427e302 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/bloc/tab_event.dart @@ -0,0 +1,12 @@ +import 'package:equatable/equatable.dart'; + +abstract class TabEvent extends Equatable { + const TabEvent(); + + @override + List get props => []; +} + +class LoadAvailableItems extends TabEvent {} + +class LoadClosedItems extends TabEvent {} diff --git a/lib/features/MainScreens/Invest/presentation/bloc/tab_state.dart b/lib/features/MainScreens/Invest/presentation/bloc/tab_state.dart new file mode 100644 index 0000000..b4e0d8e --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/bloc/tab_state.dart @@ -0,0 +1,24 @@ +import 'package:equatable/equatable.dart'; + +class TabState extends Equatable { + final List availableItems; + final List closedItems; + + const TabState({ + this.availableItems = const [], + this.closedItems = const [], + }); + + TabState copyWith({ + List? availableItems, + List? closedItems, + }) { + return TabState( + availableItems: availableItems ?? this.availableItems, + closedItems: closedItems ?? this.closedItems, + ); + } + + @override + List get props => [availableItems, closedItems]; +} diff --git a/lib/features/MainScreens/Invest/presentation/pages/investScreen.dart b/lib/features/MainScreens/Invest/presentation/pages/investScreen.dart deleted file mode 100644 index 36d8b2a..0000000 --- a/lib/features/MainScreens/Invest/presentation/pages/investScreen.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/material.dart'; - -class InvestScreen extends StatefulWidget { - const InvestScreen({super.key}); - - @override - State createState() => _InvestScreenState(); -} - -class _InvestScreenState extends State { - @override - Widget build(BuildContext context) { - return const Scaffold( - body: Text('Invest'), - ); - } -} diff --git a/lib/features/MainScreens/Invest/presentation/pages/invest_details_layout.dart b/lib/features/MainScreens/Invest/presentation/pages/invest_details_layout.dart new file mode 100644 index 0000000..6556012 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/pages/invest_details_layout.dart @@ -0,0 +1,59 @@ +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/features/MainScreens/Invest/presentation/pages/invest_video_section.dart'; +import 'package:tanami_app/features/MainScreens/Invest/presentation/widgets/invest_details_section.dart'; + +import '../widgets/invest_image_carousel.dart'; +import '../widgets/invest_included_documents_section.dart'; +import '../widgets/key_investment_section.dart'; + +class InvestDetailsLayout extends StatelessWidget { + const InvestDetailsLayout({super.key}); + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 20.0), + child: Column( + children: [ + Container( + decoration: BoxDecoration( + color: AppColor.plainWhite, + borderRadius: const BorderRadius.all(Radius.circular(20.0)), + boxShadow: [ + BoxShadow( + color: AppColor.plainBlack.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), + ), + ], + ), + child: Column( + children: [ + InvestCarouselView(), + const InvestDetailsSection(), + ], + ), + ), + Gap( + 20.h, + ), + const KeyInvestmentSection(), + Gap( + 20.h, + ), + const InvestIncludedDocumentsSection(), + Gap( + 20.h, + ), + const InvestVideoSection(), + ], + ), + ), + ); + } +} diff --git a/lib/features/MainScreens/Invest/presentation/pages/invest_details_screen.dart b/lib/features/MainScreens/Invest/presentation/pages/invest_details_screen.dart new file mode 100644 index 0000000..d495009 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/pages/invest_details_screen.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_screenutil/flutter_screenutil.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/button_widget.dart'; +import '../../../../../shared/components/appbar_widget.dart'; +import '../../../Portfolio/presentation/bloc/carousel/carousel_bloc.dart'; +import 'invest_details_layout.dart'; + +class InvestDetailsScreen extends StatelessWidget { + const InvestDetailsScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + bottomNavigationBar: Container( + padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 26), + width: 1.sw, + height: 105.h, + child: ButtonWidget().elevatedBtn( + text: AppText.investText, + clr: AppColor.primaryColor2, + function: () {}), + ), + appBar: const AppBarWidget( + height: 45, + titleTxt: "", + ), + body: MultiBlocProvider( + providers: [ + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => CarouselBloc(), + ), + ], + child: const InvestDetailsLayout(), + )); + } +} diff --git a/lib/features/MainScreens/Invest/presentation/pages/invest_layout.dart b/lib/features/MainScreens/Invest/presentation/pages/invest_layout.dart new file mode 100644 index 0000000..6ea294f --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/pages/invest_layout.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:google_fonts/google_fonts.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_text.dart'; +import 'package:tanami_app/features/MainScreens/Invest/presentation/widgets/kyc_card.dart'; + +import '../bloc/tab_bloc.dart'; +import '../bloc/tab_event.dart'; +import '../bloc/tab_state.dart'; +import '../widgets/invest_details_section.dart'; +import '../widgets/invest_image_carousel.dart'; + +class InvestLayout extends StatelessWidget { + const InvestLayout({super.key}); + + @override + Widget build(BuildContext context) { + return DefaultTabController( + length: 2, + child: Scaffold( + body: Column( + children: [ + TabBar( + tabs: const [ + Tab(text: AppText.availableText), + Tab(text: AppText.closedText), + ], + labelStyle: GoogleFonts.dmSans( + fontWeight: FontWeight.bold, + fontSize: 14.0, + ), + unselectedLabelStyle: GoogleFonts.dmSans( + fontWeight: FontWeight.normal, + fontSize: 14.0, + ), + labelColor: AppColor.plainBlack, + unselectedLabelColor: AppColor.charcoalColor, + indicatorColor: AppColor.plainBlack, + indicatorSize: TabBarIndicatorSize.tab, + indicatorWeight: 1.0, + indicatorPadding: const EdgeInsets.symmetric(horizontal: 16.0), + ), + const Expanded( + child: TabBarView( + children: [ + AvailableItemsScreen(), + ClosedItemsScreen(), + ], + ), + ), + ], + ), + ), + ); + } +} + +class AvailableItemsScreen extends StatelessWidget { + const AvailableItemsScreen({super.key}); + + @override + Widget build(BuildContext context) { + context.read().add(LoadAvailableItems()); + + return BlocBuilder( + builder: (context, state) { + if (state.availableItems.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + return ListView.builder( + itemCount: state.availableItems.length, + itemBuilder: (context, index) { + return index == 0 + ? Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 18, + ), + child: kycCard()) + : InkWell( + onTap: () { + goRouter.pushNamed(RouteName.investDetailScreen); + }, + child: Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 18, + ), + decoration: BoxDecoration( + color: AppColor.plainWhite, + borderRadius: + const BorderRadius.all(Radius.circular(20.0)), + boxShadow: [ + BoxShadow( + color: AppColor.plainBlack.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), + ), + ], + ), + child: Column( + children: [ + InvestCarouselView(), + const InvestDetailsSection(), + ], + ), + ), + ); + }, + ); + }, + ); + } +} + +class ClosedItemsScreen extends StatelessWidget { + const ClosedItemsScreen({super.key}); + + @override + Widget build(BuildContext context) { + context.read().add(LoadClosedItems()); + + return BlocBuilder( + builder: (context, state) { + if (state.closedItems.isEmpty) { + return const Center(child: CircularProgressIndicator()); + } + return ListView.builder( + itemCount: state.closedItems.length, + itemBuilder: (context, index) { + return index == 0 + ? Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 18, + ), + child: kycCard()) + : InkWell( + onTap: () { + goRouter.pushNamed(RouteName.investDetailScreen); + }, + child: Container( + margin: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 18, + ), + decoration: BoxDecoration( + color: AppColor.plainWhite, + borderRadius: + const BorderRadius.all(Radius.circular(20.0)), + boxShadow: [ + BoxShadow( + color: AppColor.plainBlack.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), + ), + ], + ), + child: Column( + children: [ + InvestCarouselView(), + const InvestDetailsSection(), + ], + ), + ), + ); + }, + ); + }, + ); + } +} diff --git a/lib/features/MainScreens/Invest/presentation/pages/invest_screen.dart b/lib/features/MainScreens/Invest/presentation/pages/invest_screen.dart new file mode 100644 index 0000000..1023769 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/pages/invest_screen.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:tanami_app/features/MainScreens/Invest/presentation/bloc/tab_bloc.dart'; +import 'package:tanami_app/features/MainScreens/Portfolio/presentation/bloc/carousel/carousel_bloc.dart'; + +import '../../../../../core/styles/app_color.dart'; +import '../../../../../core/styles/app_text.dart'; +import '../../../../../shared/components/text_widget.dart'; +import 'invest_layout.dart'; + +class InvestScreen extends StatefulWidget { + const InvestScreen({super.key}); + + @override + State createState() => _InvestScreenState(); +} + +class _InvestScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + // backgroundColor: Colors.white, + elevation: 0, + scrolledUnderElevation: 0, + automaticallyImplyLeading: false, + title: TextWidget().text22W700( + AppText.investText, + clr: AppColor.charcoalColor, + ), + titleSpacing: 16, + ), + body: MultiBlocProvider( + providers: [ + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => TabBloc(), + ), + BlocProvider( + // Create an instance of the OnboardingBloc + create: (context) => CarouselBloc(), + ), + ], + child: const InvestLayout(), + ), + ); + } +} diff --git a/lib/features/MainScreens/Invest/presentation/pages/invest_video_section.dart b/lib/features/MainScreens/Invest/presentation/pages/invest_video_section.dart new file mode 100644 index 0000000..35b798e --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/pages/invest_video_section.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:google_fonts/google_fonts.dart'; +import 'package:tanami_app/core/styles/app_images.dart'; + +import '../../../../../core/styles/app_text.dart'; + +class InvestVideoSection extends StatelessWidget { + const InvestVideoSection({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(20.0)), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), // changes position of shadow + ), + ], + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppText.videosTitle, + style: GoogleFonts.dmSans( + color: Colors.black, + fontSize: 15.sp, + fontWeight: FontWeight.w700, + ), + ), + SizedBox( + height: 12.h, + ), + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: videos.length, + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.only(bottom: 18.0), + child: ClipRRect( + borderRadius: const BorderRadius.all( + Radius.circular(18.0), + ), + child: Stack( + children: [ + Image.asset( + videos[index], + fit: BoxFit.cover, + height: 160.h, + width: 1.sw, + ), + Positioned( + bottom: 0.0, + child: Image.asset( + AppImages.academyCardOverlay, + ), + ), + Positioned.fill( + child: Image.asset( + AppImages.videoPlayIcon, + ), + ) + ], + ), + ), + ); + }, + ) + ], + ), + ), + ); + } +} + +List videos = [ + 'assets/images/academy_screen/vd_bg.jpg', + 'assets/images/academy_screen/vd_bg.jpg', + 'assets/images/academy_screen/vd_bg.jpg' +]; diff --git a/lib/features/MainScreens/Invest/presentation/widgets/invest_details_section.dart b/lib/features/MainScreens/Invest/presentation/widgets/invest_details_section.dart new file mode 100644 index 0000000..f80a133 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/widgets/invest_details_section.dart @@ -0,0 +1,155 @@ +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 '../../../../../core/styles/app_text.dart'; +import '../../../../../shared/components/text_widget.dart'; + +class InvestDetailsSection extends StatelessWidget { + const InvestDetailsSection({super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + color: AppColor.plainWhite, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextWidget().text17W700( + 'Multi Family Residental', + clr: AppColor.plainBlack, + ), + Gap( + 10.h, + ), + TextWidget() + .text22W400("SAR 1,478,000", clr: AppColor.investTextColor), + const Gap(8.0), + LinearProgressIndicator( + value: 0.6, + borderRadius: BorderRadius.circular(2), + minHeight: 8.0, + backgroundColor: AppColor.txtBorderColor, + valueColor: const AlwaysStoppedAnimation( + AppColor.investTextColor), + ), + const Gap(8.0), + TextWidget().text11W700("60% ${AppText.fundedText}", + clr: AppColor.portoflioCardTextColor), + const Gap(8.0), + TextWidget().text14W400( + 'Forem ipsum dolor sit amet, consectetur adipiscing elit. Nunc vulputate libero et velit interdum, ac aliquet odio mattis. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur tempus urna at turpis condimentum lobortis.', + clr: Colors.grey, + txtAlign: TextAlign.start, + ) + ], + ), + ), + ), + Container( + decoration: const BoxDecoration( + color: AppColor.portfolioCardBgColor, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(20.0), + bottomRight: Radius.circular(20.0), + ), + ), + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 150.w, + child: TextWidget().text14W500( + "${AppText.sponsorNameText}:", + clr: AppColor.portoflioCardTextColor, + txtAlign: TextAlign.start, + ), + ), + TextWidget().text14W700( + 'Silverlake', + clr: AppColor.plainBlack, + txtAlign: TextAlign.end, + ) + ], + ), + SizedBox( + height: 8.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 150.w, + child: TextWidget().text14W500( + "${AppText.estimatedReturnText}:", + clr: AppColor.portoflioCardTextColor, + txtAlign: TextAlign.start, + ), + ), + TextWidget().text14W700( + '20.0%', + clr: AppColor.plainBlack, + txtAlign: TextAlign.end, + ), + ], + ), + SizedBox( + height: 8.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 150.w, + child: TextWidget().text14W500( + "${AppText.holdingPeriodText}:", + clr: AppColor.portoflioCardTextColor, + txtAlign: TextAlign.start, + ), + ), + TextWidget().text14W700( + '24 Months', + clr: AppColor.plainBlack, + txtAlign: TextAlign.end, + ), + ], + ), + SizedBox( + height: 8.h, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextWidget().text14W500( + "${AppText.minimumInvestmentText}:", + clr: AppColor.portoflioCardTextColor, + txtAlign: TextAlign.start, + ), + TextWidget().text14W700( + 'SAR 1,000', + clr: AppColor.plainBlack, + txtAlign: TextAlign.end, + ), + ], + ) + ], + ), + ), + ) + ], + ); + } +} diff --git a/lib/features/MainScreens/Invest/presentation/widgets/invest_image_carousel.dart b/lib/features/MainScreens/Invest/presentation/widgets/invest_image_carousel.dart new file mode 100644 index 0000000..8072eb8 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/widgets/invest_image_carousel.dart @@ -0,0 +1,149 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:carousel_slider/carousel_slider.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/text_widget.dart'; + +import '../../../Portfolio/presentation/bloc/carousel/carousel_bloc.dart'; +import '../../../Portfolio/presentation/bloc/carousel/carousel_event.dart'; +import '../../../Portfolio/presentation/bloc/carousel/carousel_state.dart'; + +final List imgList = [ + {'id': 1, 'img_path': 'assets/images/portfolio_screen/detailsbg.png'}, + {'id': 2, 'img_path': 'assets/images/portfolio_screen/detailsbg.png'}, + {'id': 3, 'img_path': 'assets/images/portfolio_screen/detailsbg.png'}, + {'id': 4, 'img_path': 'assets/images/portfolio_screen/detailsbg.png'}, + {'id': 5, 'img_path': 'assets/images/portfolio_screen/detailsbg.png'}, +]; + +class InvestCarouselView extends StatelessWidget { + final CarouselController _controller = CarouselController(); + + InvestCarouselView({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + height: 190.h, + decoration: const BoxDecoration( + color: AppColor.portfolioCardBgColor, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20.0), + topRight: Radius.circular(20.0), + ), + ), + child: Stack( + children: [ + ClipRRect( + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20.0), + topRight: Radius.circular(20.0), + ), + child: CarouselSlider( + items: imgList + .map( + (item) => Image.asset( + item['img_path'], + fit: BoxFit.cover, + width: double.infinity, + alignment: Alignment.topCenter, + ), + ) + .toList(), + carouselController: _controller, + options: CarouselOptions( + scrollPhysics: const BouncingScrollPhysics(), + autoPlay: true, + aspectRatio: 2, + viewportFraction: 1, + onPageChanged: (index, reason) { + context.read().add(PageChanged(index)); + }), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: Padding( + padding: const EdgeInsets.only(bottom: 17.0), + child: BlocBuilder( + builder: (context, state) { + int currentIndex = 0; + if (state is CarouselPageState) { + currentIndex = state.currentIndex; + } + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: imgList.asMap().entries.map((entry) { + return GestureDetector( + onTap: () => _controller.animateToPage(entry.key), + child: Container( + width: 20.w, + height: 7.h, + margin: const EdgeInsets.symmetric(horizontal: 3.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: (currentIndex == entry.key) + ? AppColor.plainWhite + : AppColor.smokeGrayColor, + ), + ), + ); + }).toList(), + ); + }, + ), + ), + ), + Positioned( + top: 20, + left: 20, + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0), + decoration: BoxDecoration( + color: const Color(0xFFE4F5E9), + borderRadius: BorderRadius.circular(30.0), + ), + child: TextWidget().text12W700( + 'Asset Class', + clr: AppColor.selectedItemColor, + ), + ), + ), + Positioned( + top: 20, + right: 20, + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(30.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Image.asset( + AppImages.portfolioClock, + height: 15.h, + ), + Gap( + 5.w, + ), + TextWidget().text12W500( + '${AppText.closingDateText} Jul 10 2025', + clr: AppColor.plainBlack, + ), + ], + ), + ), + ) + ], + ), + ); + } +} diff --git a/lib/features/MainScreens/Invest/presentation/widgets/invest_included_documents_section.dart b/lib/features/MainScreens/Invest/presentation/widgets/invest_included_documents_section.dart new file mode 100644 index 0000000..5239042 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/widgets/invest_included_documents_section.dart @@ -0,0 +1,103 @@ +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/shared/components/text_widget.dart'; + +import '../../../../../core/styles/app_text.dart'; + +class InvestIncludedDocumentsSection extends StatelessWidget { + const InvestIncludedDocumentsSection({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: AppColor.plainWhite, + borderRadius: const BorderRadius.all(Radius.circular(20.0)), + boxShadow: [ + BoxShadow( + color: AppColor.plainBlack.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), // changes position of shadow + ), + ], + ), + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextWidget().text15W700( + AppText.includeddocs, + clr: AppColor.plainBlack, + ), + Gap( + 16.h, + ), + GridView.count( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + childAspectRatio: 1.7, + mainAxisSpacing: 10.h, + crossAxisSpacing: 10.w, + crossAxisCount: 2, // Number of columns + children: List.generate( + 6, + (index) { + return Center( + child: Container( + decoration: const BoxDecoration( + color: AppColor.documentCardBgColor, + borderRadius: BorderRadius.all(Radius.circular(10.0)), + ), + padding: const EdgeInsets.all(20.0), + child: Column( + children: [ + Row( + children: [ + Image.asset( + AppImages.documentIcon, + height: 16.sp, + ), + Gap( + 7.w, + ), + TextWidget().text12W700("Filename.pdf", + clr: AppColor.plainBlack), + ], + ), + Gap( + 14.h, + ), + Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextWidget().text12W700("512 Mb", + clr: AppColor.portoflioCardTextColor), + Gap( + 7.w, + ), + Image.asset( + AppImages.donwloadIcon, + height: 20.sp, + ), + ], + ), + ], + ), + ), + ); + }, + ), + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/MainScreens/Invest/presentation/widgets/key_investment_section.dart b/lib/features/MainScreens/Invest/presentation/widgets/key_investment_section.dart new file mode 100644 index 0000000..1aed2e2 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/widgets/key_investment_section.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.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/shared/components/text_widget.dart'; + +import '../../../../../core/styles/app_text.dart'; + +class KeyInvestmentSection extends StatelessWidget { + const KeyInvestmentSection({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + width: double.infinity, + decoration: BoxDecoration( + color: AppColor.plainWhite, + borderRadius: const BorderRadius.all(Radius.circular(20.0)), + boxShadow: [ + BoxShadow( + color: AppColor.plainBlack.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 3), // changes position of shadow + ), + ], + ), + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextWidget().text15W700( + AppText.keyMeritsIOfnvestmentText, + clr: AppColor.plainBlack, + ), + Gap( + 16.h, + ), + ListView.builder( + itemCount: 4, + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemBuilder: (ctx, index) { + return Container( + margin: const EdgeInsets.only( + bottom: 12, + ), + child: investmentSectionCardText( + AppImages.timeSquareIcon, + "Nunc vulputate libero et velit interdum", + "ac aliquet odio mattis.", + ), + ); + }) + ], + ), + ), + ); + } +} + +Widget investmentSectionCardText( + String icon, + String title, + String subtitle, +) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SvgPicture.asset(icon), + const Gap(8), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextWidget().text14W700( + title, + clr: AppColor.textLabelColor, + txtAlign: TextAlign.start, + ), + TextWidget().text14W400( + subtitle, + clr: AppColor.textLabelColor, + txtAlign: TextAlign.start, + ), + ], + ) + ], + ); +} diff --git a/lib/features/MainScreens/Invest/presentation/widgets/kyc_card.dart b/lib/features/MainScreens/Invest/presentation/widgets/kyc_card.dart new file mode 100644 index 0000000..c94c539 --- /dev/null +++ b/lib/features/MainScreens/Invest/presentation/widgets/kyc_card.dart @@ -0,0 +1,58 @@ +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'; + +Widget kycCard() { + return Container( + clipBehavior: Clip.antiAlias, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + decoration: ShapeDecoration( + color: AppColor.investKycCardColor, + shape: RoundedRectangleBorder( + side: const BorderSide(width: 1, color: AppColor.investKycBorderColor), + borderRadius: BorderRadius.circular(22), + ), + shadows: const [ + BoxShadow( + color: AppColor.investKycBoxShadow1Color, + blurRadius: 4, + offset: Offset(0, 1), + spreadRadius: 0, + ), + BoxShadow( + color: AppColor.investKycBoxShadow2Color, + blurRadius: 4, + offset: Offset(0, 8), + spreadRadius: 0, + ) + ], + ), + child: Row( + children: [ + SvgPicture.asset( + AppImages.investIcon, + ), + const Gap(8), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextWidget().text14W600( + AppText.completeYourVerification, + clr: AppColor.languageTextColor, + ), + TextWidget().text11W500( + AppText.verifyYourAccountInUnderMinutesToStartInvestingToday, + clr: AppColor.languageTextColor, + ), + ], + ), + ), + ], + ), + ); +} diff --git a/lib/features/MainScreens/Settings/presentation/widgets/privacy_settings_section.dart b/lib/features/MainScreens/Settings/presentation/widgets/privacy_settings_section.dart index e89a6c3..f94d879 100644 --- a/lib/features/MainScreens/Settings/presentation/widgets/privacy_settings_section.dart +++ b/lib/features/MainScreens/Settings/presentation/widgets/privacy_settings_section.dart @@ -40,7 +40,11 @@ class PrivacySettingsSection extends StatelessWidget { ), const Gap(12), SettingsListItem( - onTapFunc: () {}, + onTapFunc: () { + goRouter.pushNamed( + RouteName.changePasswordScreen, + ); + }, icon: AppImages.resetPasswordIcon, title: AppText.resetPasswordText, trailing: "", diff --git a/lib/features/MainScreens/Wallet/presentation/pages/walletDetails.dart b/lib/features/MainScreens/Wallet/presentation/pages/walletDetails.dart index 4474836..e5ed198 100644 --- a/lib/features/MainScreens/Wallet/presentation/pages/walletDetails.dart +++ b/lib/features/MainScreens/Wallet/presentation/pages/walletDetails.dart @@ -1,34 +1,58 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:google_fonts/google_fonts.dart'; +import 'package:tanami_app/core/styles/app_text.dart'; class WalletDetails extends StatelessWidget { final String type; WalletDetails({super.key, required this.type}); - final Map data_deposit = { - 'Payment method': 'Google/Apple Pay', - 'Account Name': 'Name Surname', - 'IBAN': 'DE 1234 5678 9012 3456', - 'Beneficiary Address': 'Hohenzollernring 58, 95444', - 'Bank Name': 'Bank Name', - 'Branch Address': 'Hohenzollernring 58, 95444', - 'SWIFT/BIC code': 'BC12345', - 'Reference ID': 'FRYU FHDU 1234', - }; - final Map data_withdraw = { - 'Status': 'On hold', - 'Payment method': 'Google/Apple Pay', - 'Account Name': 'Name Surname', - 'IBAN': 'DE 1234 5678 9012 3456', - 'Beneficiary Address': 'Hohenzollernring 58, 95444', - 'Bank Name': 'Bank Name', - 'Branch Address': 'Hohenzollernring 58, 95444', - 'SWIFT/BIC code': 'BC12345', - 'Reference ID': 'FRYU FHDU 1234', - }; - final Map data_invest = { - 'Payment method': 'Google/Apple Pay', - }; + final List titles = [ + AppText.status, + AppText.paymentMethod, + AppText.accountName, + AppText.iban, + AppText.beneficiaryAddress, + AppText.bankName, + AppText.branchAddress, + AppText.SWIFTcode, + AppText.refid, + ]; + final List values = [ + 'On hold', + 'Google/Apple Pay', + 'Name Surname', + 'DE 1234 5678 9012 3456', + 'Hohenzollernring 58, 95444', + 'Name Bank', + 'Hohenzollernring 58, 95444', + 'BC12345', + 'FRYU FHDU 1234', + ]; + + // final Map data_deposit = { + // AppText.paymentMethod: 'Google/Apple Pay', + // AppText.accountName: 'Name Surname', + // AppText.iban: 'DE 1234 5678 9012 3456', + // AppText.beneficiaryAddress: 'Hohenzollernring 58, 95444', + // AppText.bankName: 'Bank Name', + // AppText.branchAddress: 'Hohenzollernring 58, 95444', + // AppText.SWIFTcode: 'BC12345', + // AppText.refid: 'FRYU FHDU 1234', + // }; + // final Map data_withdraw = { + // AppText.status: 'On hold', + // AppText.paymentMethod: 'Google/Apple Pay', + // AppText.accountName: 'Name Surname', + // AppText.iban: 'DE 1234 5678 9012 3456', + // AppText.beneficiaryAddress: 'Hohenzollernring 58, 95444', + // AppText.bankName: 'Bank Name', + // AppText.branchAddress: 'Hohenzollernring 58, 95444', + // AppText.SWIFTcode: 'BC12345', + // AppText.refid: 'FRYU FHDU 1234', + // }; + // final Map data_invest = { + // AppText.paymentMethod: 'Google/Apple Pay', + // }; @override Widget build(BuildContext context) { return Scaffold( @@ -39,7 +63,7 @@ class WalletDetails extends StatelessWidget { scrolledUnderElevation: 0.0, centerTitle: true, title: Text( - 'Details', + AppText.walletDetailsTitle, style: GoogleFonts.dmSans( color: const Color(0xFF272727), fontSize: 20.sp, @@ -68,7 +92,7 @@ class WalletDetails extends StatelessWidget { child: Padding( padding: const EdgeInsets.fromLTRB(20.0, 30, 20, 10), child: Column( - // mainAxisSize: MainAxisSize.min, + mainAxisSize: MainAxisSize.min, children: [ Column( crossAxisAlignment: CrossAxisAlignment.center, @@ -202,99 +226,110 @@ class WalletDetails extends StatelessWidget { } Widget _buildBody() { - List> entries; - if (type == 'Deposit') { - entries = data_deposit.entries.toList(); - } else if (type == 'Withdrawal') { - entries = data_withdraw.entries.toList(); - } else if (type == 'Yield') { + // List> entries; + if (type == 'Yield') { return Container(); } else if (type == 'Refund') { return Container(); - } else { - entries = data_invest.entries.toList(); - return Padding( - padding: const EdgeInsets.symmetric(vertical: 15.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - entries[0].key, - style: GoogleFonts.dmSans( - color: const Color(0xFF535353), - fontSize: 14.sp, - fontWeight: FontWeight.w500, - ), + } else if (type == 'Investment') { + return Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Colors.white, + width: 1.0, ), - SizedBox( - width: 180.w, - child: Text( - entries[0].value, - textAlign: TextAlign.end, + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 15.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + AppText.paymentMethod, style: GoogleFonts.dmSans( - color: const Color(0xFF191B1E), + color: const Color(0xFF535353), fontSize: 14.sp, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w500, ), ), - ), - ], + SizedBox( + width: 180.w, + child: Text( + 'Google/Apple Pay', + textAlign: TextAlign.end, + style: GoogleFonts.dmSans( + color: const Color(0xFF191B1E), + fontSize: 14.sp, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ), + ); + } else { + return Expanded( + child: ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: titles.length, + itemBuilder: (context, index) { + if (type == 'Deposit' && titles[index] == 'Status') { + return Container(); + } else { + return Container( + width: double.infinity, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: (index != titles.length - 1) + ? Colors.black + : Colors.white, + width: 1.0, + ), + ), + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 15.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + titles[index], + style: GoogleFonts.dmSans( + color: const Color(0xFF535353), + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + SizedBox( + width: 180.w, + child: Text( + values[index], + textAlign: TextAlign.end, + style: GoogleFonts.dmSans( + color: (titles[index] == 'Status') + ? const Color(0xFF0172CB) + : const Color(0xFF191B1E), + fontSize: 14.sp, + fontWeight: (titles[index] == 'Status') + ? FontWeight.w700 + : FontWeight.w600, + ), + ), + ), + ], + ), + ), + ); + } + }, ), ); } - return Expanded( - child: ListView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: entries.length, - itemBuilder: (context, index) { - return Container( - width: double.infinity, - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - color: (index != entries.length - 1) - ? Colors.black - : Colors.white, - width: 1.0, - ), - ), - ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 15.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - entries[index].key, - style: GoogleFonts.dmSans( - color: const Color(0xFF535353), - fontSize: 14.sp, - fontWeight: FontWeight.w500, - ), - ), - SizedBox( - width: 180.w, - child: Text( - entries[index].value, - textAlign: TextAlign.end, - style: GoogleFonts.dmSans( - color: (entries[index].key == 'Status') - ? const Color(0xFF0172CB) - : const Color(0xFF191B1E), - fontSize: 14.sp, - fontWeight: (entries[index].key == 'Status') - ? FontWeight.w700 - : FontWeight.w600, - ), - ), - ), - ], - ), - ), - ); - }, - ), - ); } } diff --git a/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/confirmation.dart b/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/confirmation.dart index d7738b7..d7d3775 100644 --- a/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/confirmation.dart +++ b/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/confirmation.dart @@ -4,6 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:google_fonts/google_fonts.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_text.dart'; class WithdrawalConfirmation extends StatefulWidget { const WithdrawalConfirmation({super.key}); @@ -13,249 +14,260 @@ class WithdrawalConfirmation extends StatefulWidget { } class _WithdrawalConfirmationState extends State { - final Map data = { - 'Payment method': 'Google/Apple Pay', - 'Account Name': 'Name Surname', - 'IBAN': 'DE 1234 5678 9012 3456', - 'Beneficiary Address': 'Hohenzollernring 58, 95444', - 'Bank Name': 'Bank Name', - 'Branch Address': 'Hohenzollernring 58, 95444', - 'SWIFT/BIC code': 'BC12345', - 'Reference ID': 'FRYU FHDU 1234', - }; + final List titles = [ + AppText.paymentMethod, + AppText.accountName, + AppText.iban, + AppText.beneficiaryAddress, + AppText.bankName, + AppText.branchAddress, + AppText.SWIFTcode, + AppText.refid, + ]; + final List values = [ + 'Google/Apple Pay', + 'Name Surname', + 'DE 1234 5678 9012 3456', + 'Hohenzollernring 58, 95444', + 'Name Bank', + 'Hohenzollernring 58, 95444', + 'BC12345', + 'FRYU FHDU 1234', + ]; @override Widget build(BuildContext context) { - final List> entries = data.entries.toList(); - - return Scaffold( - backgroundColor: Colors.white, - appBar: AppBar( - automaticallyImplyLeading: false, + return WillPopScope( + onWillPop: () async => false, + child: Scaffold( backgroundColor: Colors.white, - elevation: 0, - scrolledUnderElevation: 0.0, - centerTitle: true, - title: Text( - 'Withdrawal confirmation', - style: GoogleFonts.dmSans( - color: const Color(0xFF272727), - fontSize: 20.sp, - fontWeight: FontWeight.w700, - ), - ), - actions: [ - IconButton( - onPressed: () { - goRouter.goNamed(RouteName.mainScreen); - }, - icon: const Icon( - Icons.close_sharp, + appBar: AppBar( + automaticallyImplyLeading: false, + backgroundColor: Colors.white, + elevation: 0, + scrolledUnderElevation: 0.0, + centerTitle: true, + title: Text( + 'Withdrawal confirmation', + style: GoogleFonts.dmSans( + color: const Color(0xFF272727), + fontSize: 20.sp, + fontWeight: FontWeight.w700, ), ), - ], - ), - body: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(18.0, 10, 18.0, 0), - child: Row( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.asset( - 'assets/images/wallet_screen/done.png', - height: 25.h, - ), - SizedBox( - width: 10.w, - ), - SizedBox( - width: 320.w, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Please confirm the withdrawal amount and verify the accuracy of your bank details.', - style: GoogleFonts.dmSans( - color: const Color(0xFF015698), - fontSize: 14.sp, - fontWeight: FontWeight.w700, - ), - ), - SizedBox( - height: 4.h, - ), - Text( - 'Processing times vary from 3-7 working days', - style: GoogleFonts.dmSans( - color: const Color(0xFF015698), - fontSize: 12.sp, - fontWeight: FontWeight.w500, - ), - ), - ], - ), - ), - ], + actions: [ + IconButton( + onPressed: () { + goRouter.goNamed(RouteName.mainScreen); + }, + icon: const Icon( + Icons.close_sharp, ), ), - SizedBox( - height: 20.h, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 18.0), - child: Container( - width: double.infinity, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(22.r), - color: Colors.white, - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.15), - spreadRadius: 2, - blurRadius: 10, - offset: const Offset(0, 8), + ], + ), + body: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(18.0, 10, 18.0, 0), + child: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + 'assets/images/wallet_screen/done.png', + height: 25.h, ), - ], - ), - child: Padding( - padding: const EdgeInsets.fromLTRB(20.0, 30, 20, 10), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.center, + SizedBox( + width: 10.w, + ), + SizedBox( + width: 320.w, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - decoration: const BoxDecoration( - shape: BoxShape.circle, - color: Color(0xFFE6681F), - ), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Image.asset( - 'assets/images/wallet_screen/withdraw_list.png', - height: 25.h, - ), - ), - ), - SizedBox( - height: 16.h, - ), Text( - 'Withdrawal', + 'Please confirm the withdrawal amount and verify the accuracy of your bank details.', style: GoogleFonts.dmSans( - color: const Color(0xFF191B1E), + color: const Color(0xFF015698), fontSize: 14.sp, - fontWeight: FontWeight.w600, + fontWeight: FontWeight.w700, ), ), SizedBox( height: 4.h, ), Text( - '10/04/2024 22:04', + 'Processing times vary from 3-7 working days', style: GoogleFonts.dmSans( - color: const Color(0xFF8D8D8D), - fontSize: 11.sp, + color: const Color(0xFF015698), + fontSize: 12.sp, fontWeight: FontWeight.w500, ), ), - SizedBox( - height: 16.h, - ), - Text( - '- SAR 1000', - style: GoogleFonts.dmSans( - color: const Color(0xFF191B1E), - fontSize: 20.sp, - fontWeight: FontWeight.w900, - ), - ), ], ), - SizedBox( - height: 35.h, - ), - ListView.builder( - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: entries.length, - itemBuilder: (context, index) { - return Column( - children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 12.0), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - entries[index].key, - style: GoogleFonts.dmSans( - color: const Color(0xFF535353), - fontSize: 14.sp, - fontWeight: FontWeight.w500, - ), - ), - SizedBox( - width: 175.w, - child: Text( - entries[index].value, - textAlign: TextAlign.end, - style: GoogleFonts.dmSans( - color: const Color(0xFF191B1E), - fontSize: 14.sp, - fontWeight: FontWeight.w600, - ), - ), - ), - ], - ), - ), - (index != entries.length - 1) - ? Divider() - : Container() - ], - ); - }, + ), + ], + ), + ), + SizedBox( + height: 20.h, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 18.0), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(22.r), + color: Colors.white, + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.15), + spreadRadius: 2, + blurRadius: 10, + offset: const Offset(0, 8), ), ], ), + child: Padding( + padding: const EdgeInsets.fromLTRB(20.0, 30, 20, 10), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + decoration: const BoxDecoration( + shape: BoxShape.circle, + color: Color(0xFFE6681F), + ), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Image.asset( + 'assets/images/wallet_screen/withdraw_list.png', + height: 25.h, + ), + ), + ), + SizedBox( + height: 16.h, + ), + Text( + 'Withdrawal', + style: GoogleFonts.dmSans( + color: const Color(0xFF191B1E), + fontSize: 14.sp, + fontWeight: FontWeight.w600, + ), + ), + SizedBox( + height: 4.h, + ), + Text( + '10/04/2024 22:04', + style: GoogleFonts.dmSans( + color: const Color(0xFF8D8D8D), + fontSize: 11.sp, + fontWeight: FontWeight.w500, + ), + ), + SizedBox( + height: 16.h, + ), + Text( + '- SAR 1000', + style: GoogleFonts.dmSans( + color: const Color(0xFF191B1E), + fontSize: 20.sp, + fontWeight: FontWeight.w900, + ), + ), + ], + ), + SizedBox( + height: 35.h, + ), + ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: titles.length, + itemBuilder: (context, index) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 12.0), + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + titles[index], + style: GoogleFonts.dmSans( + color: const Color(0xFF535353), + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + SizedBox( + width: 175.w, + child: Text( + values[index], + textAlign: TextAlign.end, + style: GoogleFonts.dmSans( + color: const Color(0xFF191B1E), + fontSize: 14.sp, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ), + (index != titles.length - 1) + ? Divider() + : Container() + ], + ); + }, + ), + ], + ), + ), ), ), - ), - SizedBox( - height: 10.h, - ), - ], - ), - ), - bottomNavigationBar: GestureDetector( - onTap: () { - goRouter.goNamed(RouteName.mainScreen); - }, - child: Container( - margin: const EdgeInsets.all(12.0), - height: 56.h, - width: double.infinity, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(22.r), - color: const Color(0xFF004717), + SizedBox( + height: 10.h, + ), + ], ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 20.0), - child: Center( - child: Text( - 'Close', - style: GoogleFonts.dmSans( - color: Colors.white, - fontSize: 14.sp, - fontWeight: FontWeight.w700, + ), + bottomNavigationBar: GestureDetector( + onTap: () { + goRouter.goNamed(RouteName.mainScreen); + }, + child: Container( + margin: const EdgeInsets.all(12.0), + height: 56.h, + width: double.infinity, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(22.r), + color: const Color(0xFF004717), + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 20.0), + child: Center( + child: Text( + 'Close', + style: GoogleFonts.dmSans( + color: Colors.white, + fontSize: 14.sp, + fontWeight: FontWeight.w700, + ), ), ), ), diff --git a/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/preview.dart b/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/preview.dart index a2bdc66..f98ae12 100644 --- a/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/preview.dart +++ b/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/preview.dart @@ -3,6 +3,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:google_fonts/google_fonts.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_text.dart'; class WithdrawalPreview extends StatefulWidget { const WithdrawalPreview({super.key}); @@ -15,12 +16,12 @@ class _WithdrawalPreviewState extends State { @override Widget build(BuildContext context) { List titles = [ - 'Account Holder Name/Beneficiary Name', - 'IBAN', - 'Beneficiary Address', - 'Bank Name', - 'Branch Address', - 'SWIFT/BIC Code', + AppText.accountHolderName, + AppText.iban, + AppText.beneficiaryAddress, + AppText.bankName, + AppText.branchAddress, + AppText.SWIFTcode, ]; List values = [ 'Name Surname', @@ -38,7 +39,7 @@ class _WithdrawalPreviewState extends State { scrolledUnderElevation: 0.0, centerTitle: true, title: Text( - 'Withdrawal confirmation', + AppText.previewTitle, style: GoogleFonts.dmSans( color: const Color(0xFF272727), fontSize: 20.sp, @@ -66,7 +67,7 @@ class _WithdrawalPreviewState extends State { SizedBox( width: 320.w, child: Text( - 'Please confirm the withdrawal amount and verify the accuracy of your bank details.', + AppText.info1, style: GoogleFonts.dmSans( color: const Color(0xFF015698), fontSize: 12.sp, @@ -83,7 +84,7 @@ class _WithdrawalPreviewState extends State { text: TextSpan( children: [ TextSpan( - text: 'Wallet Balance: ', + text: AppText.balance, style: GoogleFonts.dmSans( color: const Color(0xFF888888), fontSize: 12.sp, @@ -140,7 +141,7 @@ class _WithdrawalPreviewState extends State { width: 12.w, ), Text( - 'Withdrawal', + AppText.withdrawTitle, style: GoogleFonts.dmSans( color: Colors.black, fontSize: 17.sp, @@ -166,7 +167,7 @@ class _WithdrawalPreviewState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - 'Total Withdrawal amount:', + AppText.withdrawAmt, style: GoogleFonts.dmSans( color: const Color(0xFF535353), fontSize: 14.sp, @@ -220,7 +221,7 @@ class _WithdrawalPreviewState extends State { width: 10.w, ), Text( - 'Payment can include transfer fee from your bank', + AppText.info2, style: GoogleFonts.dmSans( color: const Color(0xFF015698), fontSize: 12.sp, @@ -239,7 +240,7 @@ class _WithdrawalPreviewState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Withdrawal details', + AppText.withdrawDetails, style: GoogleFonts.dmSans( color: Colors.black, fontSize: 15.sp, @@ -327,7 +328,7 @@ class _WithdrawalPreviewState extends State { padding: const EdgeInsets.symmetric(vertical: 20.0), child: Center( child: Text( - 'Submit request', + AppText.submit, style: GoogleFonts.dmSans( color: Colors.white, fontSize: 14.sp, diff --git a/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/withdrawalScreen.dart b/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/withdrawalScreen.dart index aafc040..e46eac2 100644 --- a/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/withdrawalScreen.dart +++ b/lib/features/MainScreens/Wallet/presentation/pages/withdrawal/withdrawalScreen.dart @@ -4,6 +4,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:google_fonts/google_fonts.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_text.dart'; class WithdrawalScreen extends StatefulWidget { const WithdrawalScreen({super.key}); @@ -16,12 +17,12 @@ class _WithdrawalScreenState extends State { @override Widget build(BuildContext context) { List titles = [ - 'Account Holder Name/Beneficiary Name *', - 'IBAN *', - 'Beneficiary Address', - 'Bank Name', - 'Branch Address', - 'SWIFT/BIC Code', + '${AppText.accountHolderName} *', + '${AppText.iban} *', + AppText.beneficiaryAddress, + AppText.bankName, + AppText.branchAddress, + AppText.SWIFTcode, ]; return Scaffold( backgroundColor: Colors.white, @@ -31,7 +32,7 @@ class _WithdrawalScreenState extends State { scrolledUnderElevation: 0.0, centerTitle: true, title: Text( - 'Enter bank details', + AppText.withdrawalScreenTitle, style: GoogleFonts.dmSans( color: const Color(0xFF272727), fontSize: 20.sp, @@ -49,7 +50,7 @@ class _WithdrawalScreenState extends State { text: TextSpan( children: [ TextSpan( - text: 'Wallet Balance: ', + text: AppText.balance, style: GoogleFonts.dmSans( color: const Color(0xFF888888), fontSize: 12.sp, @@ -106,7 +107,7 @@ class _WithdrawalScreenState extends State { width: 12.w, ), Text( - 'Withdrawal', + AppText.withdrawTitle, style: GoogleFonts.dmSans( color: Colors.black, fontSize: 17.sp, @@ -242,7 +243,7 @@ class _WithdrawalScreenState extends State { width: 10.w, ), Text( - 'Payment can include transfer fee from your bank', + AppText.info2, style: GoogleFonts.dmSans( color: const Color(0xFF015698), fontSize: 12.sp, @@ -280,7 +281,7 @@ class _WithdrawalScreenState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Withdrawal details', + AppText.withdrawDetails, style: GoogleFonts.dmSans( color: Colors.black, fontSize: 15.sp, @@ -291,7 +292,7 @@ class _WithdrawalScreenState extends State { height: 3.h, ), Text( - 'Fields with * are required', + AppText.required, style: GoogleFonts.dmSans( color: const Color(0xFF8D8D8D), fontSize: 14.sp, @@ -419,7 +420,7 @@ class _WithdrawalScreenState extends State { padding: const EdgeInsets.symmetric(vertical: 20.0), child: Center( child: Text( - 'Next', + AppText.next, style: GoogleFonts.dmSans( color: Colors.white, fontSize: 14.sp, diff --git a/lib/features/MainScreens/main_screen.dart b/lib/features/MainScreens/main_screen.dart index 3189a75..ddfafcf 100644 --- a/lib/features/MainScreens/main_screen.dart +++ b/lib/features/MainScreens/main_screen.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:tanami_app/features/MainScreens/Academy/presentation/pages/academy_screen.dart'; -import 'package:tanami_app/features/MainScreens/Invest/presentation/pages/investScreen.dart'; +import 'package:tanami_app/features/MainScreens/Invest/presentation/pages/invest_screen.dart'; import 'package:tanami_app/features/MainScreens/Portfolio/presentation/pages/portfolio_screen.dart'; import 'package:tanami_app/features/MainScreens/Settings/presentation/pages/settings_Screen.dart'; import 'package:tanami_app/features/MainScreens/Wallet/presentation/pages/walletScreen.dart'; diff --git a/lib/features/changePassword/presentation/bloc/restore_password_bloc.dart b/lib/features/changePassword/presentation/bloc/change_password_bloc.dart similarity index 57% rename from lib/features/changePassword/presentation/bloc/restore_password_bloc.dart rename to lib/features/changePassword/presentation/bloc/change_password_bloc.dart index 49d732e..1a404d1 100644 --- a/lib/features/changePassword/presentation/bloc/restore_password_bloc.dart +++ b/lib/features/changePassword/presentation/bloc/change_password_bloc.dart @@ -1,12 +1,15 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/material.dart'; -import 'restore_password_event.dart'; -import 'restore_password_state.dart'; +import 'change_password_event.dart'; +import 'change_password_state.dart'; -class RestorePasswordBloc - extends Bloc { +class ChangePasswordBloc + extends Bloc { final GlobalKey formKey = GlobalKey(); + + final TextEditingController currentPasswordTextField = + TextEditingController(); final TextEditingController passwordTextField = TextEditingController(); final TextEditingController repeatPasswordTextField = TextEditingController(); @@ -14,47 +17,51 @@ class RestorePasswordBloc return formKey; } - RestorePasswordBloc() : super(RestorePasswordInitial()) { + ChangePasswordBloc() : super(ChangePasswordInitial()) { + currentPasswordTextField.addListener(_onFormFieldChanged); passwordTextField.addListener(_onFormFieldChanged); repeatPasswordTextField.addListener(_onFormFieldChanged); - on(_onLoginFormChanged); - on((event, emit) async { + on(_onLoginFormChanged); + on((event, emit) async { if (!formKey.currentState!.validate()) { return; } - emit(RestorePasswordLoading()); + emit(ChangePasswordLoading()); try { // Simulate API call await Future.delayed(const Duration(seconds: 2)); // Replace the next line with actual API call final isSuccess = await _mockLoginApi(event.password); if (isSuccess) { - emit(RestorePasswordSuccess()); + emit(ChangePasswordSuccess()); } else { - emit(const RestorePasswordFailure( + emit(const ChangePasswordFailure( "Failed. Please check your credentials.")); } } catch (e) { - emit(RestorePasswordFailure(e.toString())); + emit(ChangePasswordFailure(e.toString())); } }); } void _onFormFieldChanged() { - add(RestorePasswordFormChanged( + add(ChangePasswordFormChanged( + currentPasswordTextField.text, passwordTextField.text, repeatPasswordTextField.text, )); } void _onLoginFormChanged( - RestorePasswordFormChanged event, Emitter emit) { - final areFieldsFilled = - event.password.isNotEmpty && event.repeatPassword.isNotEmpty; - emit(RestorePasswordFieldsState(areFieldsFilled)); + ChangePasswordFormChanged event, Emitter emit) { + final areFieldsFilled = event.currentPassword.isNotEmpty && + event.password.isNotEmpty && + event.repeatPassword.isNotEmpty; + emit(ChangePasswordFieldsState(areFieldsFilled)); } // Method to reset text fields void resetFields() { + currentPasswordTextField.clear(); passwordTextField.clear(); repeatPasswordTextField.clear(); } @@ -69,6 +76,7 @@ class RestorePasswordBloc @override Future close() { passwordTextField.dispose(); + currentPasswordTextField.dispose(); repeatPasswordTextField.dispose(); return super.close(); diff --git a/lib/features/changePassword/presentation/bloc/change_password_event.dart b/lib/features/changePassword/presentation/bloc/change_password_event.dart new file mode 100644 index 0000000..78114ac --- /dev/null +++ b/lib/features/changePassword/presentation/bloc/change_password_event.dart @@ -0,0 +1,35 @@ +import 'package:equatable/equatable.dart'; + +abstract class ChangePasswordEvent extends Equatable { + const ChangePasswordEvent(); + + @override + List get props => []; +} + +class ChangePasswordSubmitted extends ChangePasswordEvent { + final String currentPassword; + final String password; + final String repeatPassword; + + const ChangePasswordSubmitted( + this.password, + this.currentPassword, + this.repeatPassword, + ); + + @override + List get props => [currentPassword, password, repeatPassword]; +} + +class ChangePasswordFormChanged extends ChangePasswordEvent { + final String currentPassword; + final String password; + final String repeatPassword; + + const ChangePasswordFormChanged( + this.currentPassword, this.password, this.repeatPassword); + + @override + List get props => [currentPassword, password, repeatPassword]; +} diff --git a/lib/features/changePassword/presentation/bloc/change_password_state.dart b/lib/features/changePassword/presentation/bloc/change_password_state.dart new file mode 100644 index 0000000..3e34708 --- /dev/null +++ b/lib/features/changePassword/presentation/bloc/change_password_state.dart @@ -0,0 +1,32 @@ +import 'package:equatable/equatable.dart'; + +abstract class ChangePasswordState extends Equatable { + const ChangePasswordState(); + + @override + List get props => []; +} + +class ChangePasswordInitial extends ChangePasswordState {} + +class ChangePasswordLoading extends ChangePasswordState {} + +class ChangePasswordSuccess extends ChangePasswordState {} + +class ChangePasswordFailure extends ChangePasswordState { + final String error; + + const ChangePasswordFailure(this.error); + + @override + List get props => [error]; +} + +class ChangePasswordFieldsState extends ChangePasswordState { + final bool areFieldsFilled; + + const ChangePasswordFieldsState(this.areFieldsFilled); + + @override + List get props => [areFieldsFilled]; +} diff --git a/lib/features/changePassword/presentation/bloc/restore_password_event.dart b/lib/features/changePassword/presentation/bloc/restore_password_event.dart deleted file mode 100644 index 1ee3d13..0000000 --- a/lib/features/changePassword/presentation/bloc/restore_password_event.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:equatable/equatable.dart'; - -abstract class RestorePasswordEvent extends Equatable { - const RestorePasswordEvent(); - - @override - List get props => []; -} - -class RestorePasswordSubmitted extends RestorePasswordEvent { - final String password; - final String repeatPassword; - - const RestorePasswordSubmitted( - this.password, - this.repeatPassword, - ); - - @override - List get props => [password, repeatPassword]; -} - -class RestorePasswordFormChanged extends RestorePasswordEvent { - final String password; - final String repeatPassword; - - const RestorePasswordFormChanged(this.password, this.repeatPassword); - - @override - List get props => [password, repeatPassword]; -} diff --git a/lib/features/changePassword/presentation/bloc/restore_password_state.dart b/lib/features/changePassword/presentation/bloc/restore_password_state.dart deleted file mode 100644 index 97c1591..0000000 --- a/lib/features/changePassword/presentation/bloc/restore_password_state.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:equatable/equatable.dart'; - -abstract class RestorePasswordState extends Equatable { - const RestorePasswordState(); - - @override - List get props => []; -} - -class RestorePasswordInitial extends RestorePasswordState {} - -class RestorePasswordLoading extends RestorePasswordState {} - -class RestorePasswordSuccess extends RestorePasswordState {} - -class RestorePasswordFailure extends RestorePasswordState { - final String error; - - const RestorePasswordFailure(this.error); - - @override - List get props => [error]; -} - -class RestorePasswordFieldsState extends RestorePasswordState { - final bool areFieldsFilled; - - const RestorePasswordFieldsState(this.areFieldsFilled); - - @override - List get props => [areFieldsFilled]; -} diff --git a/lib/features/changePassword/presentation/pages/change_password_layout.dart b/lib/features/changePassword/presentation/pages/change_password_layout.dart index 3f4345d..353a282 100644 --- a/lib/features/changePassword/presentation/pages/change_password_layout.dart +++ b/lib/features/changePassword/presentation/pages/change_password_layout.dart @@ -1,24 +1,15 @@ import 'package:flutter/material.dart'; -import 'package:gap/gap.dart'; -import '../widgets/restore_password_bottom_section.dart'; -import '../widgets/restore_password_form.dart'; -import '../widgets/restore_password_top_section.dart'; +import '../widgets/change_password_bottom_section.dart'; +import '../widgets/change_password_form.dart'; class ChangePasswordLayout extends StatelessWidget { const ChangePasswordLayout({super.key}); @override Widget build(BuildContext context) { - return Scaffold( - body: ListView( - children: const [ - RestorePasswordTopSection(), - RestorePasswordForm(), - Gap(150), - RestorePasswordBottomSection(), - ], - ), - ); + return const Scaffold( + bottomNavigationBar: RestorePasswordBottomSection(), + body: RestorePasswordForm()); } } diff --git a/lib/features/changePassword/presentation/pages/change_password_screen.dart b/lib/features/changePassword/presentation/pages/change_password_screen.dart index 5eb91c9..b6bffdd 100644 --- a/lib/features/changePassword/presentation/pages/change_password_screen.dart +++ b/lib/features/changePassword/presentation/pages/change_password_screen.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import '../bloc/restore_password_bloc.dart'; +import '../../../../core/styles/app_text.dart'; +import '../../../../shared/components/appbar_widget.dart'; +import '../bloc/change_password_bloc.dart'; import 'change_password_layout.dart'; class ChangePasswordScreen extends StatelessWidget { @@ -10,13 +12,17 @@ class ChangePasswordScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - resizeToAvoidBottomInset: true, + appBar: const AppBarWidget( + height: 75, + titleTxt: AppText.changePasswordText, + ), + resizeToAvoidBottomInset: false, body: MultiBlocProvider( // Define the providers for the OnboardingBloc and other blocs providers: [ BlocProvider( // Create an instance of the OnboardingBloc - create: (context) => RestorePasswordBloc(), + create: (context) => ChangePasswordBloc(), ), ], child: const ChangePasswordLayout(), diff --git a/lib/features/changePassword/presentation/widgets/restore_password_bottom_section.dart b/lib/features/changePassword/presentation/widgets/change_password_bottom_section.dart similarity index 61% rename from lib/features/changePassword/presentation/widgets/restore_password_bottom_section.dart rename to lib/features/changePassword/presentation/widgets/change_password_bottom_section.dart index 5942f4a..e849273 100644 --- a/lib/features/changePassword/presentation/widgets/restore_password_bottom_section.dart +++ b/lib/features/changePassword/presentation/widgets/change_password_bottom_section.dart @@ -5,16 +5,14 @@ import 'package:gap/gap.dart'; import 'package:tanami_app/shared/components/loader.dart'; import 'package:tanami_app/shared/components/toast_message.dart'; -import '../../../../core/routes/route_name.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'; -import '../../../countrySelection/presentation/bloc/choose_country_bloc.dart'; -import '../bloc/restore_password_bloc.dart'; -import '../bloc/restore_password_event.dart'; -import '../bloc/restore_password_state.dart'; +import '../bloc/change_password_bloc.dart'; +import '../bloc/change_password_event.dart'; +import '../bloc/change_password_state.dart'; class RestorePasswordBottomSection extends StatelessWidget { const RestorePasswordBottomSection({ @@ -23,21 +21,18 @@ class RestorePasswordBottomSection extends StatelessWidget { @override Widget build(BuildContext context) { - final radioBloc = context.read(); return Column( + mainAxisSize: MainAxisSize.min, children: [ - BlocConsumer( + BlocConsumer( listener: (context, state) { - if (state is RestorePasswordLoading) { + if (state is ChangePasswordLoading) { Loader.loader(context); - } else if (state is RestorePasswordSuccess) { - successToastMessage(context, "Password resetted successful !"); + } else if (state is ChangePasswordSuccess) { + successToastMessage(context, "Password changed successful !"); goRouter.pop(); - radioBloc.resetSelection(); - goRouter.goNamed(RouteName.loginScreen, pathParameters: { - "fromScreen": "registerStep", - }); - } else if (state is RestorePasswordFailure) { + goRouter.pop(); + } else if (state is ChangePasswordFailure) { goRouter.pop(); errorToastMessage( context, @@ -47,10 +42,10 @@ class RestorePasswordBottomSection extends StatelessWidget { }, builder: (context, state) { bool isButtonEnabled = false; - if (state is RestorePasswordFieldsState) { + if (state is ChangePasswordFieldsState) { isButtonEnabled = state.areFieldsFilled; - } else if (state is RestorePasswordSuccess || - state is RestorePasswordFailure) { + } else if (state is ChangePasswordSuccess || + state is ChangePasswordFailure) { isButtonEnabled = true; } return Container( @@ -65,13 +60,21 @@ class RestorePasswordBottomSection extends StatelessWidget { : AppColor.inactiveBtnTxtColor, function: () { isButtonEnabled - ? context.read().add( - RestorePasswordSubmitted( - context - .read() - .passwordTextField - .text, - ""), + ? context.read().add( + ChangePasswordSubmitted( + context + .read() + .currentPasswordTextField + .text, + context + .read() + .passwordTextField + .text, + context + .read() + .repeatPasswordTextField + .text, + ), ) : null; }, diff --git a/lib/features/changePassword/presentation/widgets/restore_password_form.dart b/lib/features/changePassword/presentation/widgets/change_password_form.dart similarity index 53% rename from lib/features/changePassword/presentation/widgets/restore_password_form.dart rename to lib/features/changePassword/presentation/widgets/change_password_form.dart index d6be1e7..a9f5d59 100644 --- a/lib/features/changePassword/presentation/widgets/restore_password_form.dart +++ b/lib/features/changePassword/presentation/widgets/change_password_form.dart @@ -2,17 +2,22 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:tanami_app/core/styles/app_text.dart'; -import 'package:tanami_app/features/forgotPassword/presentation/bloc/restore_password_bloc.dart'; +import '../../../../core/routes/route_name.dart'; +import '../../../../core/routes/routes.dart'; +import '../../../../core/styles/app_color.dart'; import '../../../../shared/components/bloc/password_field/password_visibility_bloc.dart'; +import '../../../../shared/components/button_widget.dart'; import '../../../../shared/components/form_label_textfield.dart'; +import '../../../../shared/components/text_widget.dart'; +import '../bloc/change_password_bloc.dart'; class RestorePasswordForm extends StatelessWidget { const RestorePasswordForm({super.key}); @override Widget build(BuildContext context) { - final restorePasswordBloc = context.read(); + final restorePasswordBloc = context.read(); // Reset fields when the screen is built restorePasswordBloc.resetFields(); @@ -33,7 +38,18 @@ class RestorePasswordForm extends StatelessWidget { create: (_) => PasswordVisibilityBloc(), child: FormLabelTextField( hintText: AppText.enterPassword, - title: AppText.password, + title: AppText.currentPsswordText, + type: AppText.password.toLowerCase(), + textEditingController: + restorePasswordBloc.currentPasswordTextField, + ), + ), + const Gap(12), + BlocProvider( + create: (_) => PasswordVisibilityBloc(), + child: FormLabelTextField( + hintText: AppText.enterPassword, + title: AppText.newPasswordText, type: AppText.password.toLowerCase(), textEditingController: restorePasswordBloc.passwordTextField, ), @@ -42,13 +58,26 @@ class RestorePasswordForm extends StatelessWidget { BlocProvider( create: (_) => PasswordVisibilityBloc(), child: FormLabelTextField( - hintText: AppText.repeatPasswordText, + hintText: AppText.enterPassword, title: AppText.repeatPasswordText, type: AppText.password.toLowerCase(), textEditingController: restorePasswordBloc.repeatPasswordTextField, ), ), + const Gap(12), + Align( + alignment: Alignment.topRight, + child: ButtonWidget().textBtn( + function: () { + goRouter.pushNamed( + RouteName.forgotPasswordPhoneVerificationScreen); + }, + text: TextWidget().text15W400(AppText.forgorPassword, + clr: AppColor.forgotPassButtonColor, + textDecoration: TextDecoration.underline)), + ), + const Gap(20), ], ), ), diff --git a/lib/features/changePassword/presentation/widgets/restore_password_top_section.dart b/lib/features/changePassword/presentation/widgets/restore_password_top_section.dart deleted file mode 100644 index 4340449..0000000 --- a/lib/features/changePassword/presentation/widgets/restore_password_top_section.dart +++ /dev/null @@ -1,41 +0,0 @@ -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 RestorePasswordTopSection extends StatelessWidget { - const RestorePasswordTopSection({ - super.key, - }); - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Gap(85), - Center( - child: SvgPicture.asset( - AppImages.weclomeLogo, - ), - ), - const Gap(60), - TextWidget().text20W700( - AppText.restorePasswordText, - clr: AppColor.charcoalColor, - ), - const Gap(10), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 75), - child: TextWidget().text14W500( - AppText.createNewPasswordText, - clr: AppColor.smokeGrayColor, - ), - ), - ], - ); - } -} diff --git a/lib/features/securePin/presentation/bloc/pin_bloc.dart b/lib/features/securePin/presentation/bloc/pin_bloc.dart index 1b3039c..d3fa3a6 100644 --- a/lib/features/securePin/presentation/bloc/pin_bloc.dart +++ b/lib/features/securePin/presentation/bloc/pin_bloc.dart @@ -1,7 +1,6 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:tanami_app/core/styles/app_text.dart'; - import '../../../../core/utils/secure/secure_storage_service.dart'; part 'pin_event.dart'; @@ -12,13 +11,20 @@ class PinBloc extends Bloc { PinBloc({required this.secureStorageService}) : super(const PinState( - pin: '', pinComplete: false, isVerified: false, error: '')) { + pin: '', + pinComplete: false, + isVerified: false, + error: '', + verifiedOnce: false)) { on((event, emit) { final newPin = state.pin + event.number; if (newPin.length <= 6) { emit(state.copyWith( - pin: newPin, pinComplete: newPin.length == 6, error: '')); + pin: newPin, + pinComplete: newPin.length == 6, + error: '', + verifiedOnce: false)); if (newPin.length == 6) { add(VerifyPinPressed(newPin)); @@ -30,7 +36,10 @@ class PinBloc extends Bloc { if (state.pin.isNotEmpty) { final newPin = state.pin.substring(0, state.pin.length - 1); emit(state.copyWith( - pin: newPin, pinComplete: newPin.length == 6, error: '')); + pin: newPin, + pinComplete: newPin.length == 6, + error: '', + verifiedOnce: false)); } }); @@ -42,11 +51,12 @@ class PinBloc extends Bloc { final storedPin = await secureStorageService.read('pin_code'); if (storedPin == event.pin) { - emit(state.copyWith(isVerified: true, error: '')); + emit(state.copyWith(isVerified: true, error: '', verifiedOnce: true)); } else { emit(state.copyWith( isVerified: false, error: AppText.incorrectPinCode, + verifiedOnce: true, )); } }); diff --git a/lib/features/securePin/presentation/bloc/pin_state.dart b/lib/features/securePin/presentation/bloc/pin_state.dart index 6245a9a..549d5fb 100644 --- a/lib/features/securePin/presentation/bloc/pin_state.dart +++ b/lib/features/securePin/presentation/bloc/pin_state.dart @@ -5,12 +5,14 @@ class PinState extends Equatable { final bool pinComplete; final bool isVerified; final String error; + final bool verifiedOnce; // New variable to prevent double listener calls const PinState({ required this.pin, required this.pinComplete, required this.isVerified, required this.error, + required this.verifiedOnce, }); PinState copyWith({ @@ -18,15 +20,17 @@ class PinState extends Equatable { bool? pinComplete, bool? isVerified, String? error, + bool? verifiedOnce, }) { return PinState( pin: pin ?? this.pin, pinComplete: pinComplete ?? this.pinComplete, isVerified: isVerified ?? this.isVerified, error: error ?? this.error, + verifiedOnce: verifiedOnce ?? this.verifiedOnce, ); } @override - List get props => [pin, pinComplete, isVerified, error]; + List get props => [pin, pinComplete, isVerified, error, verifiedOnce]; } diff --git a/lib/features/securePin/presentation/widgets/pin_keypad_section.dart b/lib/features/securePin/presentation/widgets/pin_keypad_section.dart index 05f9ec1..caee038 100644 --- a/lib/features/securePin/presentation/widgets/pin_keypad_section.dart +++ b/lib/features/securePin/presentation/widgets/pin_keypad_section.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; @@ -26,10 +28,13 @@ class PinKey extends StatelessWidget { const Gap(20), BlocConsumer( listener: (context, state) { - if (state.pinComplete) { + if (state.pinComplete && + state.error.isEmpty && + !state.verifiedOnce) { if (fromScreen == "login") { goRouter.pushNamed(RouteName.mainScreen); } else if (fromScreen == "reset-pin") { + log("Running this"); successToastMessage(context, AppText.pinUpdatedSucess); goRouter.pop(); } else { diff --git a/lib/shared/components/text_widget.dart b/lib/shared/components/text_widget.dart index 1398b21..12a2ea2 100644 --- a/lib/shared/components/text_widget.dart +++ b/lib/shared/components/text_widget.dart @@ -20,6 +20,14 @@ class TextWidget { color: clr ?? AppColor.plainWhite)); } + Widget text11W700(String text, {Color? clr}) { + return Text(text, + style: GoogleFonts.dmSans( + fontSize: 11, + fontWeight: FontWeight.w700, + color: clr ?? AppColor.plainWhite)); + } + //Text Size 12 Widget text12W400(String text, {Color? clr}) { return Text(text, @@ -213,6 +221,14 @@ class TextWidget { } //Text Size 22 + Widget text22W400(String text, {Color? clr}) { + return Text(text, + style: GoogleFonts.dmSans( + fontSize: 22, + fontWeight: FontWeight.w400, + color: clr ?? AppColor.plainWhite)); + } + Widget text22W700(String text, {Color? clr}) { return Text(text, style: GoogleFonts.dmSans( diff --git a/pubspec.yaml b/pubspec.yaml index a2a6ee1..4eb7165 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -118,3 +118,5 @@ flutter: - assets/images/settings_screen/svg/ - assets/images/language_screen/ - assets/images/language_screen/png/ + - assets/images/invest_screen/ + - assets/images/invest_screen/svg/