Merge branch 'main' into meet
This commit is contained in:
7
assets/images/invest_screen/svg/scanner.svg
Normal file
7
assets/images/invest_screen/svg/scanner.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">
|
||||
<path d="M22.6315 13.0137H1.5" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20.7501 8.7779V6.82514C20.7501 4.996 19.2541 3.5 17.425 3.5H15.7812" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.38159 8.7779V6.82095C3.38159 4.98867 4.86607 3.50314 6.69835 3.50105L8.37873 3.5" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M20.7501 13.0137V17.5446C20.7501 19.3727 19.2541 20.8698 17.425 20.8698H15.7812" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M3.38159 13.0137V17.5488C3.38159 19.3811 4.86607 20.8666 6.69835 20.8687L8.37873 20.8698" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 921 B |
3
assets/images/invest_screen/svg/shield_done.svg
Normal file
3
assets/images/invest_screen/svg/shield_done.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7281 22.4137C11.8388 22.4715 11.9627 22.5009 12.0865 22.5C12.2103 22.499 12.3331 22.4686 12.4449 22.4097L16.0128 20.5025C17.0245 19.9631 17.8168 19.3601 18.435 18.6579C19.779 17.1282 20.5129 15.1758 20.4998 13.1626L20.4575 6.52198C20.4535 5.75711 19.9511 5.07461 19.2082 4.82652L12.5707 2.59956C12.1711 2.46424 11.7331 2.46718 11.3405 2.60643L4.72824 4.91281C3.9893 5.17071 3.496 5.85811 3.50002 6.62397L3.54231 13.2597C3.5554 15.2758 4.31448 17.2194 5.68062 18.7335C6.3048 19.4258 7.10415 20.02 8.12699 20.5505L11.7281 22.4137ZM10.7836 14.6089C10.9326 14.7521 11.1259 14.8227 11.3192 14.8207C11.5125 14.8198 11.7047 14.7472 11.8517 14.6021L15.7508 10.7581C16.0438 10.4688 16.0408 10.004 15.7448 9.71866C15.4478 9.4333 14.9696 9.43526 14.6766 9.72454L11.3081 13.0449L9.92885 11.7191C9.63186 11.4337 9.15467 11.4367 8.8607 11.726C8.56774 12.0152 8.57076 12.48 8.86775 12.7654L10.7836 14.6089Z" fill="#015698" fill-opacity="0.5"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
6
assets/images/invest_screen/svg/ticket.svg
Normal file
6
assets/images/invest_screen/svg/ticket.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M13.8496 4.25V6.67" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13.8496 17.7598V19.7838" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M13.8496 14.3249V9.50391" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.7021 20C20.5242 20 22 18.5426 22 16.7431V14.1506C20.7943 14.1506 19.8233 13.1917 19.8233 12.001C19.8233 10.8104 20.7943 9.85039 22 9.85039L21.999 7.25686C21.999 5.45745 20.5221 4 18.7011 4H5.29892C3.47789 4 2.00104 5.45745 2.00104 7.25686L2 9.93485C3.20567 9.93485 4.17668 10.8104 4.17668 12.001C4.17668 13.1917 3.20567 14.1506 2 14.1506V16.7431C2 18.5426 3.4758 20 5.29787 20H18.7021Z" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 982 B |
4
assets/images/invest_screen/svg/time_square.svg
Normal file
4
assets/images/invest_screen/svg/time_square.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<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="M16.3347 2.75H7.66573C4.64473 2.75 2.75073 4.889 2.75073 7.916V16.084C2.75073 19.111 4.63473 21.25 7.66573 21.25H16.3337C19.3647 21.25 21.2507 19.111 21.2507 16.084V7.916C21.2507 4.889 19.3647 2.75 16.3347 2.75Z" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M15.3912 14.0168L12.0002 11.9938V7.63281" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 586 B |
6
assets/images/invest_screen/svg/wallet.svg
Normal file
6
assets/images/invest_screen/svg/wallet.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.6389 14.3962H17.5906C16.1042 14.3953 14.8993 13.1914 14.8984 11.7049C14.8984 10.2185 16.1042 9.01458 17.5906 9.01367H21.6389" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M18.0485 11.6432H17.7369" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.74766 3H16.3911C19.2892 3 21.6388 5.34951 21.6388 8.24766V15.4247C21.6388 18.3229 19.2892 20.6724 16.3911 20.6724H7.74766C4.84951 20.6724 2.5 18.3229 2.5 15.4247V8.24766C2.5 5.34951 4.84951 3 7.74766 3Z" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.03564 7.53772H12.4346" stroke="#066123" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 908 B |
@@ -64,4 +64,7 @@ class RouteName {
|
||||
|
||||
//change password
|
||||
static const String changePasswordScreen = "changePasswordScreen";
|
||||
|
||||
//invest
|
||||
static const String investDetailScreen = "investDetailScreen";
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -158,6 +157,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 +189,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";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import 'package:bloc/bloc.dart';
|
||||
|
||||
import 'tab_event.dart';
|
||||
import 'tab_state.dart';
|
||||
|
||||
class TabBloc extends Bloc<TabEvent, TabState> {
|
||||
TabBloc() : super(const TabState()) {
|
||||
on<LoadAvailableItems>(_onLoadAvailableItems);
|
||||
on<LoadClosedItems>(_onLoadClosedItems);
|
||||
}
|
||||
|
||||
void _onLoadAvailableItems(LoadAvailableItems event, Emitter<TabState> emit) {
|
||||
// Simulate fetching available items
|
||||
final availableItems =
|
||||
List<String>.generate(10, (index) => 'Available Item $index');
|
||||
emit(state.copyWith(availableItems: availableItems));
|
||||
}
|
||||
|
||||
void _onLoadClosedItems(LoadClosedItems event, Emitter<TabState> emit) {
|
||||
// Simulate fetching closed items
|
||||
final closedItems =
|
||||
List<String>.generate(10, (index) => 'Closed Item $index');
|
||||
emit(state.copyWith(closedItems: closedItems));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class TabEvent extends Equatable {
|
||||
const TabEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class LoadAvailableItems extends TabEvent {}
|
||||
|
||||
class LoadClosedItems extends TabEvent {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class TabState extends Equatable {
|
||||
final List<String> availableItems;
|
||||
final List<String> closedItems;
|
||||
|
||||
const TabState({
|
||||
this.availableItems = const [],
|
||||
this.closedItems = const [],
|
||||
});
|
||||
|
||||
TabState copyWith({
|
||||
List<String>? availableItems,
|
||||
List<String>? closedItems,
|
||||
}) {
|
||||
return TabState(
|
||||
availableItems: availableItems ?? this.availableItems,
|
||||
closedItems: closedItems ?? this.closedItems,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object> get props => [availableItems, closedItems];
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class InvestScreen extends StatefulWidget {
|
||||
const InvestScreen({super.key});
|
||||
|
||||
@override
|
||||
State<InvestScreen> createState() => _InvestScreenState();
|
||||
}
|
||||
|
||||
class _InvestScreenState extends State<InvestScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Scaffold(
|
||||
body: Text('Invest'),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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<TabBloc>().add(LoadAvailableItems());
|
||||
|
||||
return BlocBuilder<TabBloc, TabState>(
|
||||
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<TabBloc>().add(LoadClosedItems());
|
||||
|
||||
return BlocBuilder<TabBloc, TabState>(
|
||||
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(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<InvestScreen> createState() => _InvestScreenState();
|
||||
}
|
||||
|
||||
class _InvestScreenState extends State<InvestScreen> {
|
||||
@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(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
];
|
||||
@@ -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<Color>(
|
||||
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,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<CarouselBloc>().add(PageChanged(index));
|
||||
}),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(bottom: 17.0),
|
||||
child: BlocBuilder<CarouselBloc, CarouselState>(
|
||||
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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -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: "",
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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<RestorePasswordEvent, RestorePasswordState> {
|
||||
class ChangePasswordBloc
|
||||
extends Bloc<ChangePasswordEvent, ChangePasswordState> {
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
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<RestorePasswordFormChanged>(_onLoginFormChanged);
|
||||
on<RestorePasswordSubmitted>((event, emit) async {
|
||||
on<ChangePasswordFormChanged>(_onLoginFormChanged);
|
||||
on<ChangePasswordSubmitted>((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<RestorePasswordState> emit) {
|
||||
final areFieldsFilled =
|
||||
event.password.isNotEmpty && event.repeatPassword.isNotEmpty;
|
||||
emit(RestorePasswordFieldsState(areFieldsFilled));
|
||||
ChangePasswordFormChanged event, Emitter<ChangePasswordState> 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<void> close() {
|
||||
passwordTextField.dispose();
|
||||
currentPasswordTextField.dispose();
|
||||
repeatPasswordTextField.dispose();
|
||||
|
||||
return super.close();
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class ChangePasswordEvent extends Equatable {
|
||||
const ChangePasswordEvent();
|
||||
|
||||
@override
|
||||
List<Object> 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<Object> 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<Object> get props => [currentPassword, password, repeatPassword];
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class ChangePasswordState extends Equatable {
|
||||
const ChangePasswordState();
|
||||
|
||||
@override
|
||||
List<Object> 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<Object> get props => [error];
|
||||
}
|
||||
|
||||
class ChangePasswordFieldsState extends ChangePasswordState {
|
||||
final bool areFieldsFilled;
|
||||
|
||||
const ChangePasswordFieldsState(this.areFieldsFilled);
|
||||
|
||||
@override
|
||||
List<Object> get props => [areFieldsFilled];
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class RestorePasswordEvent extends Equatable {
|
||||
const RestorePasswordEvent();
|
||||
|
||||
@override
|
||||
List<Object> get props => [];
|
||||
}
|
||||
|
||||
class RestorePasswordSubmitted extends RestorePasswordEvent {
|
||||
final String password;
|
||||
final String repeatPassword;
|
||||
|
||||
const RestorePasswordSubmitted(
|
||||
this.password,
|
||||
this.repeatPassword,
|
||||
);
|
||||
|
||||
@override
|
||||
List<Object> get props => [password, repeatPassword];
|
||||
}
|
||||
|
||||
class RestorePasswordFormChanged extends RestorePasswordEvent {
|
||||
final String password;
|
||||
final String repeatPassword;
|
||||
|
||||
const RestorePasswordFormChanged(this.password, this.repeatPassword);
|
||||
|
||||
@override
|
||||
List<Object> get props => [password, repeatPassword];
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
abstract class RestorePasswordState extends Equatable {
|
||||
const RestorePasswordState();
|
||||
|
||||
@override
|
||||
List<Object> 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<Object> get props => [error];
|
||||
}
|
||||
|
||||
class RestorePasswordFieldsState extends RestorePasswordState {
|
||||
final bool areFieldsFilled;
|
||||
|
||||
const RestorePasswordFieldsState(this.areFieldsFilled);
|
||||
|
||||
@override
|
||||
List<Object> get props => [areFieldsFilled];
|
||||
}
|
||||
@@ -1,24 +1,17 @@
|
||||
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());
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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<RadioBloc>();
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
BlocConsumer<RestorePasswordBloc, RestorePasswordState>(
|
||||
BlocConsumer<ChangePasswordBloc, ChangePasswordState>(
|
||||
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<RestorePasswordBloc>().add(
|
||||
RestorePasswordSubmitted(
|
||||
context
|
||||
.read<RestorePasswordBloc>()
|
||||
.passwordTextField
|
||||
.text,
|
||||
""),
|
||||
? context.read<ChangePasswordBloc>().add(
|
||||
ChangePasswordSubmitted(
|
||||
context
|
||||
.read<ChangePasswordBloc>()
|
||||
.currentPasswordTextField
|
||||
.text,
|
||||
context
|
||||
.read<ChangePasswordBloc>()
|
||||
.passwordTextField
|
||||
.text,
|
||||
context
|
||||
.read<ChangePasswordBloc>()
|
||||
.repeatPasswordTextField
|
||||
.text,
|
||||
),
|
||||
)
|
||||
: null;
|
||||
},
|
||||
@@ -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<RestorePasswordBloc>();
|
||||
final restorePasswordBloc = context.read<ChangePasswordBloc>();
|
||||
|
||||
// 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),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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<PinEvent, PinState> {
|
||||
|
||||
PinBloc({required this.secureStorageService})
|
||||
: super(const PinState(
|
||||
pin: '', pinComplete: false, isVerified: false, error: '')) {
|
||||
pin: '',
|
||||
pinComplete: false,
|
||||
isVerified: false,
|
||||
error: '',
|
||||
verifiedOnce: false)) {
|
||||
on<NumberPressed>((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<PinEvent, PinState> {
|
||||
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<PinEvent, PinState> {
|
||||
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,
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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<Object> get props => [pin, pinComplete, isVerified, error];
|
||||
List<Object> get props => [pin, pinComplete, isVerified, error, verifiedOnce];
|
||||
}
|
||||
|
||||
@@ -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<PinBloc, PinState>(
|
||||
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 {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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/
|
||||
|
||||
Reference in New Issue
Block a user