feat: plan view, wip plan view model

This commit is contained in:
Prajna Prayoga 2024-02-16 20:32:34 +07:00
parent 3c1f7e210a
commit 0762a8ab0c
15 changed files with 989 additions and 55 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/icons/icon-thumb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -37,6 +37,8 @@ class PathAssets {
static const String iconHouse = 'assets/icons/icon-house.png'; static const String iconHouse = 'assets/icons/icon-house.png';
static const String iconToga = 'assets/icons/icon-toga.png'; static const String iconToga = 'assets/icons/icon-toga.png';
static const String iconCreatePlan = 'assets/icons/icon-create-plan.png'; static const String iconCreatePlan = 'assets/icons/icon-create-plan.png';
static const String iconThumb = 'assets/icons/icon-thumb.png';
static const String iconPortofolio = 'assets/icons/icon-portofolio.png';
/// IMAGE /// IMAGE
static const String imgSplashLogo = 'assets/images/splash-logo.png'; static const String imgSplashLogo = 'assets/images/splash-logo.png';

View File

@ -9,7 +9,7 @@ class ButtonView extends StatelessWidget {
final double? height, width, widthSuffix, widthPrefix, marginVertical; final double? height, width, widthSuffix, widthPrefix, marginVertical;
final EdgeInsetsGeometry? contentPadding; final EdgeInsetsGeometry? contentPadding;
final bool isSecondaryColor, isOutlined, heightWrapContent, disabled; final bool isSecondaryColor, isOutlined, heightWrapContent, disabled;
final Color? backgroundColor, textColor, borderColor; final Color? backgroundColor, textColor, borderColor, disabledBgColor;
final MainAxisAlignment? mainAxisAlignmentContent; final MainAxisAlignment? mainAxisAlignmentContent;
// final _widthBtn = SizeConfig.screenWidth / 1.5; // final _widthBtn = SizeConfig.screenWidth / 1.5;
final _widthBtn = SizeConfig.width * .9; final _widthBtn = SizeConfig.width * .9;
@ -34,6 +34,7 @@ class ButtonView extends StatelessWidget {
this.backgroundColor, this.backgroundColor,
this.borderColor, this.borderColor,
this.textColor, this.textColor,
this.disabledBgColor,
this.textWeight = FontWeight.bold, this.textWeight = FontWeight.bold,
this.textSize, this.textSize,
this.textAlign = TextAlign.center, this.textAlign = TextAlign.center,
@ -65,7 +66,7 @@ class ButtonView extends StatelessWidget {
height: heightWrapContent ? null : height ?? _heightBtn, height: heightWrapContent ? null : height ?? _heightBtn,
child: ElevatedButton( child: ElevatedButton(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
disabledBackgroundColor: isOutlined ? Colors.white : color.surface, disabledBackgroundColor: disabledBgColor ?? (isOutlined ? Colors.white : color.surface),
padding: contentPadding, padding: contentPadding,
backgroundColor: backgroundColor ?? backgroundColor: backgroundColor ??
(isOutlined (isOutlined

View File

@ -11,7 +11,8 @@ class GoalInvest {
} }
class GoalInvestingView extends StatelessWidget { class GoalInvestingView extends StatelessWidget {
const GoalInvestingView({super.key}); final void Function(String) onListSelected;
const GoalInvestingView({super.key, required this.onListSelected});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -28,6 +29,9 @@ class GoalInvestingView extends StatelessWidget {
return Padding( return Padding(
padding: EdgeInsets.only(top: e.key != 0 ? 16 : 0), padding: EdgeInsets.only(top: e.key != 0 ? 16 : 0),
child: ListTile( child: ListTile(
onTap: () {
onListSelected(e.value.title);
},
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
side: BorderSide(color: ColorPalette.slate200), side: BorderSide(color: ColorPalette.slate200),
borderRadius: BorderRadius.circular(14) borderRadius: BorderRadius.circular(14)

View File

@ -36,6 +36,18 @@ class NumericPad extends StatelessWidget {
], ],
), ),
dividerGradient(true, Alignment.centerLeft, Alignment.centerRight), dividerGradient(true, Alignment.centerLeft, Alignment.centerRight),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
numberWidget('7'),
dividerGradient(false, Alignment.center, Alignment.center, fullColor: true),
numberWidget('8'),
dividerGradient(false, Alignment.center, Alignment.center, fullColor: true),
numberWidget('9')
],
),
dividerGradient(true, Alignment.centerLeft, Alignment.centerRight),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@ -54,7 +66,7 @@ class NumericPad extends StatelessWidget {
Widget dividerGradient(bool isHorizontal, AlignmentGeometry gradientFrom, AlignmentGeometry gradientTo, {bool fullColor = false}) { Widget dividerGradient(bool isHorizontal, AlignmentGeometry gradientFrom, AlignmentGeometry gradientTo, {bool fullColor = false}) {
return Container( return Container(
width: isHorizontal ? SizeConfig.width : 1, width: isHorizontal ? SizeConfig.width : 1,
height: isHorizontal ? 1 : SizeConfig.height * 0.11, height: isHorizontal ? 1 : SizeConfig.height * 0.097,
decoration: BoxDecoration( decoration: BoxDecoration(
gradient: LinearGradient( gradient: LinearGradient(
colors: [ colors: [
@ -100,9 +112,14 @@ class NumericPad extends StatelessWidget {
Widget removeWidget() { Widget removeWidget() {
return Expanded( return Expanded(
child: GestureDetector(
onTap: () {
onNumberSelected('');
},
child: Icon( child: Icon(
Icons.highlight_remove, Icons.highlight_remove,
size: 28, size: 28,
),
) )
); );
} }

View File

@ -0,0 +1,212 @@
import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/core/utils/size_config.dart';
import 'package:cims_apps/features/auth/registration/view/submission_data/risk_profile/risk_profile_view_model/risk_profile_view_model.dart';
import 'package:flutter/material.dart';
class RiskProfile extends StatelessWidget {
final int totalScore;
final bool rowSuitableProduct;
const RiskProfile({super.key, required this.totalScore, required this.rowSuitableProduct});
@override
Widget build(BuildContext context) {
List<RiskProfileResult> listRiskProfileResult = [
RiskProfileResult(
'Conservative',
PathAssets.imgCat,
ColorPalette.green500,
'Investors with a conservative risk profile are risk-averse or do not want to experience large losses. Therefore, mutual fund products that are suitable for conservative investors are products that have low risk and stable returns.',
[
{'desc': 'Money Market Mutual Fund', 'icon': PathAssets.iconStrongBox},
{'desc': 'Fixed Income Mutual Fund', 'icon': PathAssets.iconMoneyReceive},
{'desc': 'Balanced Mutual Fund', 'icon': PathAssets.iconBalance},
]
),
RiskProfileResult(
'Moderate',
PathAssets.imgDeer,
ColorPalette.orange500,
'Investors with a moderate risk profile are investors who are ready to accept moderate risk to get higher returns than conservative mutual fund products. Therefore, mutual fund products that are suitable for moderate investors are products that have moderate risk and higher returns than conservative mutual fund products.',
[
{'desc': 'Fixed Income Mutual Fund', 'icon': PathAssets.iconMoneyReceive},
{'desc': 'Balanced Mutual Fund', 'icon': PathAssets.iconBalance},
]
),
RiskProfileResult(
'Aggressive',
PathAssets.imgLion,
ColorPalette.purple500,
'Investors with an aggressive risk profile are investors who are ready to accept high risks to get high returns. Therefore, mutual fund products that are suitable for aggressive investors are products that have high risk and high returns.',
[
{'desc': 'Equity Mutual Fund', 'icon': PathAssets.iconCoins},
{'desc': 'Aggressive Balanced Fund', 'icon': PathAssets.iconBalance},
]
)
];
RiskProfileResult riskProfile;
if(totalScore <= 25){
riskProfile = listRiskProfileResult[0];
}else if(totalScore <= 50){
riskProfile = listRiskProfileResult[1];
}else{
riskProfile = listRiskProfileResult[2];
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Container(
decoration: BoxDecoration(
color: riskProfile.color,
image: DecorationImage(image: AssetImage(riskProfile.img), alignment: Alignment.centerRight),
boxShadow: [
BoxShadow(
color: riskProfile.color.withOpacity(0.2),
blurRadius: 30
)
]
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
padding: EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
riskProfile.type,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 24,
color: ColorPalette.white
),
),
SizedBox(height: 16,),
Text('Total Score :',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: ColorPalette.white
),
),
Text('$totalScore',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 28,
color: ColorPalette.white
),
)
],
),
),
],
),
),
),
SizedBox(
height: 24,
),
Text(
riskProfile.desc,
style: TextStyle(
color: ColorPalette.slate500,
fontSize: 16
)
),
SizedBox(
height: 24,
),
Text(
'Suitable Product',
style: TextStyle(
color: ColorPalette.slate800,
fontWeight: FontWeight.bold,
fontSize: 18
),
),
SizedBox(
height: 16,
),
rowSuitableProduct ?
Row(
children: riskProfile.suitableProduct.asMap().entries.map((e) {
return Expanded(
child: Container(
margin: EdgeInsets.only(left: e.key != 0 ? 12 : 0),
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
border: Border.all(color: ColorPalette.slate200),
borderRadius: BorderRadius.circular(6)
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: riskProfile.color.withOpacity(0.1)
),
child: Image.asset(e.value['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color)
),
SizedBox(
height: 12,
),
Text(e.value['desc'],
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: ColorPalette.slate800
),
)
],
),
)
);
}).toList(),
)
: Wrap(
runSpacing: 16,
children: riskProfile.suitableProduct.map((e) {
return Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6),
border: Border.all(color: ColorPalette.slate200),
),
child: Row(
children: [
Container(
padding: EdgeInsets.all(8),
alignment: Alignment.center,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: riskProfile.color.withOpacity(0.1)
),
child: Image.asset(e['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color)
),
SizedBox(
width: 12,
),
Expanded(
child: Text(e['desc'],
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: ColorPalette.slate800
),
),
)
],
),
);
}).toList(),
)
],
);
}
}

View File

@ -22,7 +22,7 @@ class RiskProfileView extends StatelessWidget {
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
BackButtonView(), const BackButtonView(),
const Text('Risk Profile', textAlign: TextAlign.center), const Text('Risk Profile', textAlign: TextAlign.center),
SizedBox(width: SizeConfig.width * 0.1) SizedBox(width: SizeConfig.width * 0.1)
], ],
@ -33,11 +33,11 @@ class RiskProfileView extends StatelessWidget {
body: Container( body: Container(
width: SizeConfig.width, width: SizeConfig.width,
height: SizeConfig.height, height: SizeConfig.height,
padding: EdgeInsets.all(24), padding: const EdgeInsets.all(24),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Column( const Column(
children: [ children: [
ImageView(image: PathAssets.imgDataReport), ImageView(image: PathAssets.imgDataReport),
SizedBox( SizedBox(
@ -66,7 +66,7 @@ class RiskProfileView extends StatelessWidget {
), ),
Column( Column(
children: [ children: [
Row( const Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
ImageView( ImageView(
@ -86,13 +86,13 @@ class RiskProfileView extends StatelessWidget {
) )
], ],
), ),
SizedBox( const SizedBox(
height: 24, height: 24,
), ),
ButtonView( ButtonView(
name: "Let's Start", name: "Let's Start",
onPressed: () { onPressed: () {
routePush(context, page: QuestionView()); routePush(context, page: const QuestionView());
}, },
marginVertical: 0, marginVertical: 0,
) )

View File

@ -1,8 +1,10 @@
import 'package:cims_apps/application/theme/color_palette.dart'; import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/homepage/homepage_view.dart'; import 'package:cims_apps/features/dashboard/dashboard_account/view/homepage/homepage_view.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/plan_view.dart'; import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view/plan_view.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart'; import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BottomNavigationView extends StatefulWidget { class BottomNavigationView extends StatefulWidget {
const BottomNavigationView({Key? key}) : super(key: key); const BottomNavigationView({Key? key}) : super(key: key);
@ -48,7 +50,9 @@ class _BottomNavigationViewState extends State<BottomNavigationView> {
), ),
]; ];
return Scaffold( return ChangeNotifierProvider(
create: (context) => PlanViewModel(),
child: Scaffold(
body: listWidget[_selectedIndex], body: listWidget[_selectedIndex],
bottomNavigationBar: Padding( bottomNavigationBar: Padding(
padding: const EdgeInsets.symmetric(vertical: 12), padding: const EdgeInsets.symmetric(vertical: 12),
@ -69,6 +73,7 @@ class _BottomNavigationViewState extends State<BottomNavigationView> {
unselectedLabelStyle: const TextStyle(color: Colors.black), unselectedLabelStyle: const TextStyle(color: Colors.black),
), ),
), ),
),
); );
} }
} }

View File

@ -1,22 +0,0 @@
import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart';
import 'package:cims_apps/application/component/goal_investing_view.dart';
import 'package:flutter/material.dart';
class PlanView extends StatelessWidget {
const PlanView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(height: 70, title: 'Investment Plan'),
body: SingleChildScrollView(
padding: EdgeInsets.all(24),
child: Column(
children: [
GoalInvestingView()
],
),
),
);
}
}

View File

@ -0,0 +1,206 @@
import 'package:cims_apps/application/component/button/button_view.dart';
import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart';
import 'package:cims_apps/application/component/goal_investing_view.dart';
import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart';
import 'package:cims_apps/application/component/risk_profile.dart';
import 'package:cims_apps/application/component/text_form/text_form_view.dart';
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/core/utils/number_formatter.dart';
import 'package:cims_apps/core/utils/size_config.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/options_starting_invest.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class PlanView extends StatefulWidget {
const PlanView({super.key});
@override
State<PlanView> createState() => _PlanViewState();
}
class _PlanViewState extends State<PlanView> {
TextEditingController inputController = TextEditingController();
@override
void dispose() {
// TODO: implement dispose
super.dispose();
inputController.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(height: 70, title: 'Investment Plan'),
body: SingleChildScrollView(
padding: EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RiskProfile(
totalScore: 26,
rowSuitableProduct: true
),
SizedBox(
height: 32,
),
Text('Your Goal in Investing',
style: TextStyle(
fontWeight: FontWeight.w700,
color: ColorPalette.slate800,
fontSize: 18
),
),
SizedBox(
height: 24,
),
GoalInvestingView(
onListSelected: (p0) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return modalInvest(context, p0);
},
);
},
)
],
),
),
);
}
Widget modalInvest(context, String text) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16)
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 18),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("It's time to invest",
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600
),
),
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.close_rounded)
)
],
),
),
Divider(color: ColorPalette.slate200, height: 1),
Padding(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(text,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
),
),
Row(
children: [
Icon(Icons.change_circle_outlined, color: ColorPalette.primary, size: 20),
SizedBox(width: 4),
Text('Change',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ColorPalette.primary
),
)
],
)
],
),
TextField(
controller: inputController,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.w600,
color: ColorPalette.slate800
),
keyboardType: TextInputType.number,
onChanged: (value) {
value = value.replaceAll('Rp ', '').replaceAll('.', '');
double parseValue = double.parse(value);
if(value.isNotEmpty){
inputController.text = NumberFormatter.numberCurrency(parseValue, 'Rp ', 'id_ID', decimalDigits: 0);
}else{
inputController.text = NumberFormatter.numberCurrency(0, 'Rp ', 'id_ID', decimalDigits: 0);
}
},
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: ColorPalette.primary,
width: 2
),
)
),
),
SizedBox(height: 12),
Text('Minimum Budget Rp1,000,000',
style: TextStyle(
color: ColorPalette.slate400,
fontSize: 16,
fontWeight: FontWeight.w600
),
),
SizedBox(height: 16),
NumericPad(onNumberSelected: (p0) {
String checkIsZeroInput = inputController.text.replaceAll('Rp ', '').replaceAll(',', '');
String getNumeric = p0;
if(p0.isNotEmpty){
if(checkIsZeroInput != '0'){
getNumeric = checkIsZeroInput + getNumeric;
}
}else{
getNumeric = checkIsZeroInput.substring(0, checkIsZeroInput.length - 1);
}
String formatNumeric = NumberFormatter.numberCurrency(
double.parse(getNumeric), 'Rp ', 'id_ID', decimalDigits: 0).replaceAll('.', ',');
inputController.text = formatNumeric;
}),
SizedBox(height: 8),
ButtonView(
name: 'Next',
onPressed: () {
Navigator.pop(context);
int formatIntParse = int.parse(inputController.text.replaceAll('Rp ', '').replaceAll(',', ''));
showModalBottomSheet(context: context, builder: (context) => OptionsStartingInvest(totalInvest: formatIntParse));
},
width: SizeConfig.width,
heightWrapContent: true,
contentPadding: EdgeInsets.symmetric(vertical: 16),
marginVertical: 0,
)
],
),
),
],
),
);
}
}

View File

@ -0,0 +1,123 @@
import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/result_options_product.dart';
import 'package:flutter/material.dart';
class OptionsStartingInvest extends StatelessWidget {
final int totalInvest;
const OptionsStartingInvest({super.key, required this.totalInvest});
@override
Widget build(BuildContext context) {
List<dynamic> listOptions = [
{
"title": "Results From Your Risk Profile",
"subtitle": "Start Investing from recommended products that suit you",
"iconImg": PathAssets.iconThumb,
"value": "risk profile"
},
{
"title": "Create Portfolio",
"subtitle": "Choose a portfolio according to your investment goals",
"iconImg": PathAssets.iconPortofolio,
"value": "investment goals"
}
];
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16)
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Your options for starting to invest',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600
),
),
GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(Icons.close_rounded),
)
],
),
),
SizedBox(height: 8),
...listOptions.asMap().entries.map((e) {
return GestureDetector(
onTap: () {
Navigator.pop(context);
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => ResultOptionsProduct(totalInvest: totalInvest));
},
child: Container(
margin: const EdgeInsets.only(left: 24, right: 24, bottom: 12),
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14),
border: Border.all(color: ColorPalette.slate200),
),
child: Row(
children: [
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
padding: EdgeInsets.all(8),
alignment: Alignment.center,
decoration: BoxDecoration(
color: ColorPalette.blue200.withOpacity(0.5),
borderRadius: BorderRadius.circular(8)
),
child: Image.asset(e.value['iconImg'], width: 22),
),
SizedBox(width: 16),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(e.value['title'],
style: TextStyle(
fontWeight: FontWeight.w600,
color: ColorPalette.slate800,
fontSize: 16
),
),
SizedBox(height: 4),
Text(e.value['subtitle'],
style: TextStyle(
color: ColorPalette.slate400
),
)
],
),
)
],
),
),
Icon(Icons.keyboard_arrow_right_rounded, color: ColorPalette.slate400,)
],
),
),
);
}),
SizedBox(height: 16,)
],
),
);
}
}

View File

@ -0,0 +1,154 @@
import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/component/button/button_view.dart';
import 'package:cims_apps/application/component/image/image_view.dart';
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/core/utils/size_config.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/result_total_product.dart';
import 'package:flutter/material.dart';
class Product {
String name, type;
double totalPercent;
Product(this.name, this.type, this.totalPercent);
}
class ResultOptionsProduct extends StatelessWidget {
final int totalInvest;
const ResultOptionsProduct({super.key, required this.totalInvest});
@override
Widget build(BuildContext context) {
List<Product> listProduct = [
Product('Gemilang Dana Kas Maxima', 'Money Market', 0.7),
Product('Gemilang Dana Likuid', 'Bonds', 0.2),
Product('Gemilang Kas 2 Kelas A', 'Shares', 0.1)
];
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16)
),
padding: const EdgeInsets.all(24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(Icons.arrow_back, color: ColorPalette.slate500),
Text('Results from your risk profile',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 18,
color: ColorPalette.slate800
),
),
const Icon(Icons.close_rounded, color: ColorPalette.slate400)
],
),
const SizedBox(height: 32),
SingleChildScrollView(
child: Column(
children: listProduct.asMap().entries.map((e) {
return Container(
margin: const EdgeInsets.only(bottom: 16),
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(color: ColorPalette.slate200),
borderRadius: BorderRadius.circular(12),
boxShadow: [
const BoxShadow(
color: Color(0XFF1E293B0A)
)
]
),
child: Column(
children: [
Row(
children: [
const ImageView(image: PathAssets.iconGoogle, width: 30,),
const SizedBox(
width: 12,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(e.value.name,
style: const TextStyle(
fontWeight: FontWeight.w700,
color: ColorPalette.slate800
),
),
const SizedBox(height: 4,),
Container(
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
decoration: BoxDecoration(
border: Border.all(color: ColorPalette.investTypeColor[e.value.type]!),
color: ColorPalette.investTypeBgColor[e.value.type],
borderRadius: BorderRadius.circular(40)
),
child: Text(e.value.type,
style: TextStyle(
color: ColorPalette.investTypeColor[e.value.type],
fontWeight: FontWeight.w600
),
),
)
],
),
),
Text('${(e.value.totalPercent * 100).toInt()} %',
style: const TextStyle(
fontWeight: FontWeight.w700,
fontSize: 16,
color: ColorPalette.slate800
),
)
],
),
const Padding(
padding: EdgeInsets.symmetric(vertical: 16),
child: Divider(height: 1, color: ColorPalette.slate200),
),
GestureDetector(
onTap: () {
},
child: const Text('See More',
style: TextStyle(
color: ColorPalette.slate500,
fontWeight: FontWeight.w600,
),
),
)
],
),
);
}).toList(),
),
),
const SizedBox(
height: 16,
),
ButtonView(
name: 'Next',
onPressed: () {
Navigator.pop(context);
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) => ResultTotalProduct(listProduct: listProduct, totalInvest: totalInvest)
);
},
width: SizeConfig.width,
heightWrapContent: true,
contentPadding: const EdgeInsets.symmetric(vertical: 16),
marginVertical: 0,
)
],
),
);
}
}

View File

@ -0,0 +1,221 @@
import 'package:cims_apps/application/component/button/button_view.dart';
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/core/utils/number_formatter.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/result_options_product.dart';
import 'package:flutter/material.dart';
class ResultTotalProduct extends StatelessWidget {
final int totalInvest;
final List<Product> listProduct;
const ResultTotalProduct({super.key, required this.listProduct, required this.totalInvest});
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16)
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.all(24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Your Investment Today',
style: TextStyle(
color: ColorPalette.slate800,
fontWeight: FontWeight.w600,
fontSize: 16
),
),
GestureDetector(
onTap: () => Navigator.pop(context),
child: Icon(Icons.close_rounded)
)
],
),
),
...listProduct.asMap().entries.map((e) {
return Container(
padding: EdgeInsets.only(left: 16, right: 24, bottom: 16, top: 16),
decoration: BoxDecoration(
border: Border(
left: BorderSide(
color: ColorPalette.investTypeColor[e.value.type]!,
width: 8,
)
)
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
flex: 5,
child: Text(e.value.name,
style: TextStyle(
color: ColorPalette.slate400,
fontWeight: FontWeight.w600,
fontSize: 16
),
)
),
Expanded(
flex: 7,
child: Text(
NumberFormatter.numberCurrency(totalInvest / e.value.totalPercent, 'Rp ', 'id_ID'),
textAlign: TextAlign.end,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 18,
color: ColorPalette.slate800
),
)
)
],
),
);
}).toList(),
SizedBox(height: 16),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 24),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Purchase Commission',
style: TextStyle(
color: ColorPalette.slate400,
fontWeight: FontWeight.w600,
fontSize: 16
),
),
Text('Free',
style: TextStyle(
color: ColorPalette.primary,
fontSize: 18,
fontWeight: FontWeight.w700
),
)
],
),
),
SizedBox(height: 16,),
Container(
color: ColorPalette.slate200.withOpacity(0.5),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Total',
style: TextStyle(
color: ColorPalette.slate400,
fontWeight: FontWeight.w600,
fontSize: 16
),
),
Text(NumberFormatter.numberCurrency(totalInvest, 'Rp ', 'id_ID'),
textAlign: TextAlign.end,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 18,
color: ColorPalette.slate800
),
)
],
),
),
buttonAgreement(),
Container(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Total Payment',
style: TextStyle(
color: ColorPalette.slate400,
fontWeight: FontWeight.w600,
fontSize: 16
),
),
Text(NumberFormatter.numberCurrency(totalInvest, 'Rp ', 'id_ID'),
textAlign: TextAlign.end,
style: TextStyle(
fontWeight: FontWeight.w700,
fontSize: 18,
color: ColorPalette.slate800
),
)
],
),
),
ButtonView(
disabled: true,
name: 'Subscribe Now',
onPressed: () {
},
disabledBgColor: ColorPalette.slate200.withOpacity(0.5),
textColor: ColorPalette.slate400,
textWeight: FontWeight.w700,
textSize: 20,
backgroundColor: ColorPalette.primary,
)
],
),
);
}
Widget buttonAgreement() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 24,
width: 24,
child: Radio(
value: false,
groupValue: false,
onChanged: (value) {
},
activeColor: ColorPalette.primary,
),
),
SizedBox(width: 12,),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('I agree to buy the mutual fund on this page and have read and agreed to all the contents of the Prospectus and summary information and understand the risks of my investment decision.',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ColorPalette.slate400
),
),
GestureDetector(
onTap: () {
},
child: Text('Read More',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
decoration: TextDecoration.underline,
color: ColorPalette.primary
),
)
)
],
)
)
],
),
);
}
}

View File

@ -0,0 +1,11 @@
import 'package:flutter/material.dart';
class PlanViewModel extends ChangeNotifier {
bool isAgreement = false;
void setAgreement() {
isAgreement = !isAgreement;
notifyListeners();
}
}