Compare commits

..

6 Commits

13 changed files with 440 additions and 274 deletions

View File

@ -0,0 +1,119 @@
import 'package:cims_apps/application/component/expandable_widget/see_more_less_widget.dart';
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/core/utils/size_config.dart';
import 'package:flutter/material.dart';
class ExpandableWidget extends StatelessWidget {
final String? content;
final double? fontSize;
final int maxLinesToShow;
final Alignment? alignmentMore;
final bool? hideTextMore;
final bool? hideIconMore;
ExpandableWidget({
super.key,
required this.content,
this.fontSize,
this.maxLinesToShow = 1,
this.alignmentMore,
this.hideTextMore = false,
this.hideIconMore = true,
});
ValueNotifier<bool> expanded = ValueNotifier(false);
@override
Widget build(BuildContext context) {
final TextSpan textSpan = TextSpan(
text: content ?? "",
style: TextStyle(
fontSize: fontSize ?? 16.0,
color: ColorPalette.slate400,
),
);
final TextPainter textPainter = TextPainter(
text: textSpan,
maxLines: expanded.value ? null : maxLinesToShow,
textDirection: TextDirection.ltr,
strutStyle: StrutStyle(
fontSize: fontSize ?? 16.0,
)
);
textPainter.layout(maxWidth: SizeConfig.width);
final int numberOfLines = textPainter.computeLineMetrics().length;
return ValueListenableBuilder(
valueListenable: expanded,
builder: (context, values, _) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
if (!expanded.value && numberOfLines >= maxLinesToShow) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
content ?? "",
maxLines: maxLinesToShow,
overflow: TextOverflow.ellipsis,
style: TextStyle(
fontSize: fontSize ?? 16.0,
color: ColorPalette.slate400,
),
),
/* See More :: type 1 - See More | 2 - See Less */
SeeMoreLessWidget(
textData: 'See More',
type: 1,
section: 1,
onSeeMoreLessTap: () {
expanded.value = true;
},
alignment: alignmentMore,
hideIconMore: hideIconMore!,
hideTextMore: hideTextMore!,
),
/* See More :: type 1 - See More | 2 - See Less */
],
);
} else {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
content ?? "",
style: TextStyle(
fontSize: fontSize ?? 16.0,
color: ColorPalette.slate400,
),
),
if (expanded.value && numberOfLines >= maxLinesToShow)
/* See Less :: type 1 - See More | 2 - See Less */
SeeMoreLessWidget(
textData: 'See Less',
type: 2,
section: 1,
onSeeMoreLessTap: () {
expanded.value = false;
},
alignment: alignmentMore,
hideIconMore: hideIconMore!,
hideTextMore: hideTextMore!,
),
/* See Less :: type 1 - See More | 2 - See Less */
],
);
}
},
),
],
);
},
);
}
}

View File

@ -0,0 +1,74 @@
import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:flutter/material.dart';
class SeeMoreLessWidget extends StatelessWidget {
final String? textData;
final int? type; /* type 1 - See More | 2 - See Less */
final Function? onSeeMoreLessTap;
final int?
section; /* 1: About the course | 2 - Who can take up this course? | 3 - Mentors | 4 - Course Video Reviews */
final Alignment? alignment;
final bool hideTextMore;
final bool hideIconMore;
const SeeMoreLessWidget({
super.key,
required this.textData,
required this.type,
required this.onSeeMoreLessTap,
required this.section,
required this.alignment,
required this.hideTextMore,
required this.hideIconMore,
});
@override
Widget build(BuildContext context) {
return Align(
alignment: alignment ?? Alignment.centerLeft,
child: InkWell(
onTap: () {
if (onSeeMoreLessTap != null) {
onSeeMoreLessTap!();
}
},
child: Text.rich(
softWrap: true,
style: const TextStyle(
color: ColorPalette.primary,
),
textAlign: TextAlign.start,
TextSpan(
text: "",
children: [
if(!hideTextMore)
TextSpan(
text: textData,
style: const TextStyle(
color: ColorPalette.primary,
decoration: TextDecoration.underline,
),
),
if(!hideTextMore && !hideIconMore)
const WidgetSpan(
child: SizedBox(
width: 3.0,
),
),
if(!hideIconMore)
WidgetSpan(
child: Icon(
(type == 1)
? Icons.keyboard_arrow_down
: Icons.keyboard_arrow_up,
color: ColorPalette.slate300,
size: 28,
),
),
],
),
),
),
);
}
}

View File

@ -1,3 +1,4 @@
import 'package:cims_apps/application/component/expandable_widget/expandable_widget.dart';
import 'package:cims_apps/application/theme/color_palette.dart'; import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -44,28 +45,11 @@ class RadioAgreement extends StatelessWidget {
width: 12, width: 12,
), ),
Expanded( Expanded(
child: Column( child: ExpandableWidget(
crossAxisAlignment: CrossAxisAlignment.start, content: desc,
children: [ maxLinesToShow: 3,
Text( )
desc, )
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: ColorPalette.slate400),
),
GestureDetector(
onTap: () {},
child: const Text(
'Read More',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
decoration: TextDecoration.underline,
color: ColorPalette.primary),
))
],
))
], ],
), ),
); );

View File

@ -1,4 +1,5 @@
import 'package:cims_apps/application/assets/path_assets.dart'; import 'package:cims_apps/application/assets/path_assets.dart';
import 'package:cims_apps/application/component/expandable_widget/expandable_widget.dart';
import 'package:cims_apps/application/theme/color_palette.dart'; import 'package:cims_apps/application/theme/color_palette.dart';
import 'package:cims_apps/core/utils/size_config.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:cims_apps/features/auth/registration/view/submission_data/risk_profile/risk_profile_view_model/risk_profile_view_model.dart';
@ -73,20 +74,20 @@ class RiskProfile extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Container( Container(
padding: EdgeInsets.all(24), padding: const EdgeInsets.all(24),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
riskProfile.type, riskProfile.type,
style: TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 24, fontSize: 24,
color: ColorPalette.white color: ColorPalette.white
), ),
), ),
SizedBox(height: 16,), const SizedBox(height: 16,),
Text('Total Score :', const Text('Total Score :',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 16, fontSize: 16,
@ -94,7 +95,7 @@ class RiskProfile extends StatelessWidget {
), ),
), ),
Text('$totalScore', Text('$totalScore',
style: TextStyle( style: const TextStyle(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 28, fontSize: 28,
color: ColorPalette.white color: ColorPalette.white
@ -107,20 +108,20 @@ class RiskProfile extends StatelessWidget {
), ),
), ),
), ),
SizedBox( const SizedBox(
height: 24, height: 24,
), ),
Text( ExpandableWidget(
riskProfile.desc, content: riskProfile.desc,
style: TextStyle( hideTextMore: true,
color: ColorPalette.slate500, hideIconMore: false,
fontSize: 16 maxLinesToShow: 4,
) alignmentMore: Alignment.center,
), ),
SizedBox( const SizedBox(
height: 24, height: 24,
), ),
Text( const Text(
'Suitable Product', 'Suitable Product',
style: TextStyle( style: TextStyle(
color: ColorPalette.slate800, color: ColorPalette.slate800,
@ -128,7 +129,7 @@ class RiskProfile extends StatelessWidget {
fontSize: 18 fontSize: 18
), ),
), ),
SizedBox( const SizedBox(
height: 16, height: 16,
), ),
rowSuitableProduct ? rowSuitableProduct ?
@ -137,7 +138,7 @@ class RiskProfile extends StatelessWidget {
return Expanded( return Expanded(
child: Container( child: Container(
margin: EdgeInsets.only(left: e.key != 0 ? 12 : 0), margin: EdgeInsets.only(left: e.key != 0 ? 12 : 0),
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
border: Border.all(color: ColorPalette.slate200), border: Border.all(color: ColorPalette.slate200),
borderRadius: BorderRadius.circular(6) borderRadius: BorderRadius.circular(6)
@ -146,18 +147,18 @@ class RiskProfile extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Container( Container(
padding: EdgeInsets.all(8), padding: const EdgeInsets.all(8),
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
color: riskProfile.color.withOpacity(0.1) color: riskProfile.color.withOpacity(0.1)
), ),
child: Image.asset(e.value['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color) child: Image.asset(e.value['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color)
), ),
SizedBox( const SizedBox(
height: 12, height: 12,
), ),
Text(e.value['desc'], Text(e.value['desc'],
style: TextStyle( style: const TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: ColorPalette.slate800 color: ColorPalette.slate800
@ -173,7 +174,7 @@ class RiskProfile extends StatelessWidget {
runSpacing: 16, runSpacing: 16,
children: riskProfile.suitableProduct.map((e) { children: riskProfile.suitableProduct.map((e) {
return Container( return Container(
padding: EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
border: Border.all(color: ColorPalette.slate200), border: Border.all(color: ColorPalette.slate200),
@ -181,7 +182,7 @@ class RiskProfile extends StatelessWidget {
child: Row( child: Row(
children: [ children: [
Container( Container(
padding: EdgeInsets.all(8), padding: const EdgeInsets.all(8),
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
shape: BoxShape.circle, shape: BoxShape.circle,
@ -189,12 +190,12 @@ class RiskProfile extends StatelessWidget {
), ),
child: Image.asset(e['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color) child: Image.asset(e['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color)
), ),
SizedBox( const SizedBox(
width: 12, width: 12,
), ),
Expanded( Expanded(
child: Text(e['desc'], child: Text(e['desc'],
style: TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
color: ColorPalette.slate800 color: ColorPalette.slate800

View File

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:cims_apps/application/component/button/button_view.dart'; import 'package:cims_apps/application/component/button/button_view.dart';
import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart'; import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart';
import 'package:cims_apps/application/theme/color_palette.dart'; import 'package:cims_apps/application/theme/color_palette.dart';
@ -6,9 +8,13 @@ import 'package:cims_apps/core/utils/size_config.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class InputInvestmentView extends StatefulWidget { class InputInvestmentView extends StatefulWidget {
final String selectedPlan; final String? currentPlan;
final void Function()? changePlan;
final int? currentPrice;
final int? minimumPrice;
final int? maximumPrice;
final void Function(String value) nextMove; final void Function(String value) nextMove;
const InputInvestmentView({super.key, required this.selectedPlan, required this.nextMove}); const InputInvestmentView({super.key, required this.nextMove, this.currentPlan, this.minimumPrice, this.maximumPrice, this.currentPrice, this.changePlan});
@override @override
State<InputInvestmentView> createState() => _InputInvestmentViewState(); State<InputInvestmentView> createState() => _InputInvestmentViewState();
@ -17,10 +23,35 @@ class InputInvestmentView extends StatefulWidget {
class _InputInvestmentViewState extends State<InputInvestmentView> { class _InputInvestmentViewState extends State<InputInvestmentView> {
TextEditingController inputController = TextEditingController(); TextEditingController inputController = TextEditingController();
void validationInputValue(String currentValue) {
currentValue = currentValue.replaceAll('Rp ', '').replaceAll('.', '');
if(currentValue.isEmpty){
currentValue = '0';
}
double parseValue = double.parse(currentValue);
if(widget.minimumPrice != null){
if(parseValue <= widget.minimumPrice!){
parseValue = widget.minimumPrice!.toDouble();
}
}
if(widget.maximumPrice != null){
if(parseValue >= widget.maximumPrice!){
parseValue = widget.maximumPrice!.toDouble();
}
}
inputController.text = NumberFormatter.numberCurrency(parseValue, 'Rp ', 'id_ID', decimalDigits: 0);
}
@override @override
void initState() { void initState() {
// TODO: implement initState // TODO: implement initState
inputController.text = 'Rp 0'; if(widget.currentPrice != null){
inputController.text = NumberFormatter.numberCurrency(widget.currentPrice, 'Rp ', 'id_ID', decimalDigits: 0);
}else{
inputController.text = 'Rp 0';
}
super.initState(); super.initState();
} }
@ -41,55 +72,53 @@ class _InputInvestmentViewState extends State<InputInvestmentView> {
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
SizedBox(height: 16),
Padding( Padding(
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Row( if(widget.currentPlan != null)
mainAxisAlignment: MainAxisAlignment.spaceBetween, Row(
children: [ mainAxisAlignment: MainAxisAlignment.spaceBetween,
Text(widget.selectedPlan, children: [
style: TextStyle( Text(widget.currentPlan ?? '',
fontSize: 20, style: const TextStyle(
fontWeight: FontWeight.w700, fontSize: 20,
fontWeight: FontWeight.w700,
),
), ),
), InkWell(
Row( borderRadius: BorderRadius.circular(16),
children: [ onTap: widget.changePlan,
Icon(Icons.change_circle_outlined, color: ColorPalette.primary, size: 20), child: const Row(
SizedBox(width: 4), children: [
Text('Change', Icon(Icons.change_circle_outlined, color: ColorPalette.primary, size: 20),
style: TextStyle( SizedBox(width: 4),
fontSize: 16, Text('Change',
fontWeight: FontWeight.w600, style: TextStyle(
color: ColorPalette.primary fontSize: 16,
), fontWeight: FontWeight.w600,
) color: ColorPalette.primary
], ),
) )
], ],
), ),
)
],
),
TextField( TextField(
controller: inputController, controller: inputController,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: const TextStyle(
fontSize: 28, fontSize: 28,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: ColorPalette.slate800 color: ColorPalette.slate800
), ),
keyboardType: TextInputType.number, keyboardType: TextInputType.number,
onChanged: (value) { onChanged: (value) {
value = value.replaceAll('Rp ', '').replaceAll('.', ''); validationInputValue(value);
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( decoration: const InputDecoration(
enabledBorder: UnderlineInputBorder( enabledBorder: UnderlineInputBorder(
borderSide: BorderSide( borderSide: BorderSide(
color: ColorPalette.primary, color: ColorPalette.primary,
@ -98,30 +127,36 @@ class _InputInvestmentViewState extends State<InputInvestmentView> {
) )
), ),
), ),
SizedBox(height: 12), const SizedBox(height: 12),
Text('Minimum Budget Rp1,000,000', if(widget.minimumPrice != null)
style: TextStyle( Text('Minimum ${NumberFormatter.numberCurrency(widget.minimumPrice, 'Rp ', 'id_ID')}',
color: ColorPalette.slate400, style: const TextStyle(
fontSize: 16, color: ColorPalette.slate400,
fontWeight: FontWeight.w600 fontSize: 16,
fontWeight: FontWeight.w600
),
), ),
), if(widget.maximumPrice != null)
SizedBox(height: 16), Text('Maximum ${NumberFormatter.numberCurrency(widget.maximumPrice, 'Rp ', 'id_ID')}',
style: const TextStyle(
color: ColorPalette.slate400,
fontSize: 16,
fontWeight: FontWeight.w600
),
),
const SizedBox(height: 16),
NumericPad(onNumberSelected: (p0) { NumericPad(onNumberSelected: (p0) {
String checkIsZeroInput = inputController.text.replaceAll('Rp ', '').replaceAll(',', ''); String currentValue = inputController.text;
String getNumeric = p0;
if(p0.isNotEmpty){ if(p0.isNotEmpty){
if(checkIsZeroInput != '0'){ if(currentValue != '0'){
getNumeric = checkIsZeroInput + getNumeric; currentValue = currentValue + p0;
} }
}else{ }else{
getNumeric = checkIsZeroInput.substring(0, checkIsZeroInput.length - 1); currentValue = currentValue.substring(0, currentValue.length - 1);
} }
String formatNumeric = NumberFormatter.numberCurrency( validationInputValue(currentValue);
double.parse(getNumeric), 'Rp ', 'id_ID', decimalDigits: 0).replaceAll('.', ',');
inputController.text = formatNumeric;
}), }),
SizedBox(height: 8), const SizedBox(height: 24),
ButtonView( ButtonView(
name: 'Next', name: 'Next',
onPressed: () { onPressed: () {
@ -129,7 +164,7 @@ class _InputInvestmentViewState extends State<InputInvestmentView> {
}, },
width: SizeConfig.width, width: SizeConfig.width,
heightWrapContent: true, heightWrapContent: true,
contentPadding: EdgeInsets.symmetric(vertical: 16), contentPadding: const EdgeInsets.symmetric(vertical: 16),
marginVertical: 0, marginVertical: 0,
) )
], ],
@ -137,6 +172,6 @@ class _InputInvestmentViewState extends State<InputInvestmentView> {
), ),
], ],
), ),
);; );
} }
} }

View File

@ -139,9 +139,8 @@ class TotalPaymentView extends StatelessWidget {
), ),
RadioAgreement( RadioAgreement(
isAgree: isAgree, isAgree: isAgree,
desc: '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. Read More', desc: '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.',
onTap: () { onTap: () {
print('gagaga');
onTapAgree(); onTapAgree();
}, },
), ),

View File

@ -1,12 +1,8 @@
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/custom_app_bar/custom_app_bar.dart';
import 'package:cims_apps/application/component/subscribe/goal_investing_view.dart'; import 'package:cims_apps/application/component/subscribe/goal_investing_view.dart';
import 'package:cims_apps/application/component/subscribe/input_investment_view.dart'; import 'package:cims_apps/application/component/subscribe/input_investment_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/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/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/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/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:cims_apps/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart';
@ -46,33 +42,32 @@ class _PlanViewState extends State<PlanView> {
appBar: CustomAppBar( appBar: CustomAppBar(
height: SizeConfig.height * 0.08, height: SizeConfig.height * 0.08,
title: 'Investment Plan', title: 'Investment Plan',
leading: SizedBox(), leading: const SizedBox(),
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
padding: EdgeInsets.all(24), padding: const EdgeInsets.all(24),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
RiskProfile( const RiskProfile(
totalScore: 26, totalScore: 26,
rowSuitableProduct: true rowSuitableProduct: true
), ),
SizedBox( const SizedBox(
height: 32, height: 32,
), ),
Text('Your Goal in Investing', const Text('Your Goal in Investing',
style: TextStyle( style: TextStyle(
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
color: ColorPalette.slate800, color: ColorPalette.slate800,
fontSize: 18 fontSize: 18
), ),
), ),
SizedBox( const SizedBox(
height: 24, height: 24,
), ),
GoalInvestingView( GoalInvestingView(
onListSelected: (p0) { onListSelected: (p0) {
print(p0);
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,
@ -103,7 +98,7 @@ class _PlanViewState extends State<PlanView> {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
Text("It's time to invest", const Text("It's time to invest",
style: TextStyle( style: TextStyle(
fontSize: 16, fontSize: 16,
fontWeight: FontWeight.w600 fontWeight: FontWeight.w600
@ -113,17 +108,20 @@ class _PlanViewState extends State<PlanView> {
onTap: () { onTap: () {
Navigator.pop(context); Navigator.pop(context);
}, },
child: Icon(Icons.close_rounded) child: const Icon(Icons.close_rounded)
) )
], ],
), ),
), ),
Divider(color: ColorPalette.slate200, height: 1), const Divider(color: ColorPalette.slate200, height: 1),
InputInvestmentView( InputInvestmentView(
selectedPlan: text, currentPlan: text,
changePlan: () {
Navigator.pop(context);
},
nextMove: (value) { nextMove: (value) {
Navigator.pop(context); Navigator.pop(context);
int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll(',', '')); int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll('.', ''));
showModalBottomSheet(context: context, builder: (context) => OptionsStartingInvest(totalInvest: formatIntParse)); showModalBottomSheet(context: context, builder: (context) => OptionsStartingInvest(totalInvest: formatIntParse));
}, },
), ),

View File

@ -4,6 +4,7 @@ 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/component/image/image_view.dart';
import 'package:cims_apps/application/component/text_title/text_title.dart'; import 'package:cims_apps/application/component/text_title/text_title.dart';
import 'package:cims_apps/application/theme/color_palette.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/core/utils/size_config.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart'; import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart';
import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart';
@ -18,29 +19,33 @@ class PortfolioDetailView extends StatelessWidget {
List<PortfolioProduct> listProduct = [ List<PortfolioProduct> listProduct = [
PortfolioProduct( PortfolioProduct(
name: 'Gemilang Dana Kas Maxima', name: 'Gemilang Dana Kas Maxima',
type: '', type: 'Money Market',
yield: 8.17, yield: 8.17,
priceUnit: 2600.79, priceUnit: 2600.79,
funds: 6300000), funds: 6300000,
totalUnit: 14520
),
PortfolioProduct( PortfolioProduct(
name: 'Gemilang Dana Likuid', name: 'Gemilang Dana Likuid',
type: '', type: 'Sharia',
yield: 6.42, yield: 6.42,
priceUnit: 1600.79, priceUnit: 1600.79,
funds: 2340000), funds: 2340000,
totalUnit: 232,
),
PortfolioProduct( PortfolioProduct(
name: 'Gemilang Income Fund', name: 'Gemilang Income Fund',
type: '', type: 'Bonds',
yield: 8.17, yield: 8.17,
priceUnit: 2600.79, priceUnit: 2600.79,
funds: 6300000) funds: 6300000,
totalUnit: 2450,
)
]; ];
return MultiProvider( return ChangeNotifierProvider(
providers: [ create: (context) => RedeemProductViewModel(),
ChangeNotifierProvider(create: (context) => RedeemProductViewModel())
],
child: Scaffold( child: Scaffold(
backgroundColor: Colors.white, backgroundColor: Colors.white,
body: SizedBox( body: SizedBox(
@ -100,19 +105,20 @@ class PortfolioDetailView extends StatelessWidget {
), ),
const SizedBox(height: 24,), const SizedBox(height: 24,),
Expanded( Expanded(
child: ClipRRect( child: Container(
borderRadius: BorderRadius.circular(0), decoration: BoxDecoration(
child: Container( borderRadius: BorderRadius.circular(12),
decoration: BoxDecoration( color: Colors.white,
borderRadius: BorderRadius.circular(12), ),
color: Colors.white, child: Consumer<RedeemProductViewModel>(
), builder: (context, provider, child) {
child: ListView( return ListView(
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),
children: [ children: listProduct.asMap().entries.map((e) {
cardPortfolio(context) return cardPortfolio(context, e.value);
], }).toList(),
), );
}
), ),
) )
) )
@ -125,7 +131,7 @@ class PortfolioDetailView extends StatelessWidget {
); );
} }
Widget cardPortfolio(context) { Widget cardPortfolio(context, PortfolioProduct product) {
return Container( return Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -155,19 +161,19 @@ class PortfolioDetailView extends StatelessWidget {
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const TextTitle(title: 'Gemilang Dana Kas Maxima', fontSize: 16,), TextTitle(title: product.name ?? '', fontSize: 16,),
const SizedBox(height: 4), const SizedBox(height: 4),
Container( Container(
padding: const EdgeInsets.all(6), padding: const EdgeInsets.all(6),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40), borderRadius: BorderRadius.circular(40),
color: ColorPalette.investTypeBgColor['Money Market']?.withOpacity(0.5) ?? Colors.white, color: ColorPalette.investTypeBgColor[product.type!]?.withOpacity(0.5) ?? Colors.white,
border: Border.all(width: 2, color: ColorPalette.investTypeColor['Money Market']?.withOpacity(0.4) ?? Colors.white) border: Border.all(width: 2, color: ColorPalette.investTypeColor[product.type!]?.withOpacity(0.4) ?? Colors.white)
), ),
child: Text( child: Text(
'Money Market' ?? '', product.type ?? '',
style: TextStyle( style: TextStyle(
color: ColorPalette.investTypeColor['Money Market'], color: ColorPalette.investTypeColor[product.type!],
fontWeight: FontWeight.w600 fontWeight: FontWeight.w600
), ),
), ),
@ -187,8 +193,8 @@ class PortfolioDetailView extends StatelessWidget {
rowDescription('Present Value', 'Rp2.660.706', fontWeight: FontWeight.w700), rowDescription('Present Value', 'Rp2.660.706', fontWeight: FontWeight.w700),
rowDescription('Investment Capital', 'Rp2.660.706'), rowDescription('Investment Capital', 'Rp2.660.706'),
rowDescription('Advantages', 'Rp2.660.706'), rowDescription('Advantages', 'Rp2.660.706'),
rowDescription('Purchase Price', 'Rp1.500,57'), rowDescription('Purchase Price', NumberFormatter.numberCurrency(product.priceUnit, 'Rp ', 'id_ID')),
rowDescription('Number of Units', '14.002'), rowDescription('Number of Units', '${product.totalUnit ?? 0}'),
], ],
), ),
const SizedBox(height: 16,), const SizedBox(height: 16,),
@ -206,6 +212,7 @@ class PortfolioDetailView extends StatelessWidget {
borderColor: ColorPalette.red600, borderColor: ColorPalette.red600,
textColor: ColorPalette.red600, textColor: ColorPalette.red600,
onPressed: () { onPressed: () {
Provider.of<RedeemProductViewModel>(context, listen: false).setProduct(product);
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
isScrollControlled: true, isScrollControlled: true,

View File

@ -4,6 +4,7 @@ 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/button/button_view.dart';
import 'package:cims_apps/application/component/image/image_view.dart'; import 'package:cims_apps/application/component/image/image_view.dart';
import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart'; import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart';
import 'package:cims_apps/application/component/subscribe/input_investment_view.dart';
import 'package:cims_apps/application/component/text_title/text_title.dart'; import 'package:cims_apps/application/component/text_title/text_title.dart';
import 'package:cims_apps/application/theme/color_palette.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/number_formatter.dart';
@ -82,98 +83,33 @@ class _ChangeAmountState extends State<ChangeAmount> {
), ),
), ),
const Divider(height: 1, color: ColorPalette.slate200,), const Divider(height: 1, color: ColorPalette.slate200,),
Padding( Column(
padding: EdgeInsets.all(24), crossAxisAlignment: CrossAxisAlignment.start,
child: Column( children: [
crossAxisAlignment: CrossAxisAlignment.start, Padding(
children: [ padding: EdgeInsets.all(24),
cardProduct(), child: cardProduct()
SizedBox(height: 24), ),
TextField( InputInvestmentView(
controller: amountController, minimumPrice: (provider.getCurrentProduct.priceUnit! * 1).toInt(),
textAlign: TextAlign.center, maximumPrice: (provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!).toInt(),
style: const TextStyle( currentPrice: provider.getAmount!.toInt(),
fontSize: 28, nextMove: (value) {
fontWeight: FontWeight.w600, String formatValueInput = value.replaceAll('Rp ', '').replaceAll('.', '');
color: ColorPalette.slate800 provider.setAmount(double.parse(formatValueInput));
), Navigator.pop(context);
keyboardType: TextInputType.number, showModalBottomSheet(
onChanged: (value) { context: context,
value = value.replaceAll('Rp ', '').replaceAll('.', ''); isScrollControlled: true,
double parseValue = double.parse(value); builder: (context) {
if(value.isNotEmpty){ return const RedeemProduct();
amountController.text = NumberFormatter.numberCurrency(parseValue, 'Rp ', 'id_ID', decimalDigits: 0); },
}else{ );
amountController.text = NumberFormatter.numberCurrency(0, 'Rp ', 'id_ID', decimalDigits: 0); },
} )
}, ],
decoration: const InputDecoration( ),
enabledBorder: UnderlineInputBorder( SizedBox(height: 16,)
borderSide: BorderSide(
color: ColorPalette.primary,
width: 2
),
)
),
),
SizedBox(height: 12),
Text(
'Min Redeem: ${(NumberFormatter.numberCurrency(10000, 'Rp ', 'id_ID', decimalDigits: 0))}',
style: TextStyle(
color: ColorPalette.slate400,
fontWeight: FontWeight.w600,
),
),
Text(
'Max Redeem: ${(NumberFormatter.numberCurrency((provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!).toInt(), 'Rp ', 'id_ID', decimalDigits: 0))}',
style: TextStyle(
color: ColorPalette.slate400,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 24),
NumericPad(onNumberSelected: (p0) {
String checkIsZeroInput = amountController.text.replaceAll('Rp ', '').replaceAll('.', '');
String getNumeric = p0;
if(p0.isNotEmpty){
if(checkIsZeroInput != '0'){
getNumeric = checkIsZeroInput + getNumeric;
}
}else{
getNumeric = checkIsZeroInput.substring(0, checkIsZeroInput.length - 1);
}
if(getNumeric.isEmpty){
getNumeric = '0';
}
if(double.parse(getNumeric) >= provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!){
getNumeric = (provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!).toString();
}
String formatNumeric = NumberFormatter.numberCurrency(
double.parse(getNumeric).toInt(), 'Rp ', 'id_ID', decimalDigits: 0);
amountController.text = formatNumeric;
}),
const SizedBox(height: 24),
ButtonView(
name: 'Confirm',
textSize: 20,
marginVertical: 0,
onPressed: () {
String formatValueInput = amountController.text.replaceAll('Rp ', '').replaceAll('.', '');
provider.setAmount(double.parse(formatValueInput));
Navigator.pop(context);
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (context) {
return const RedeemProduct();
},
);
},
)
],
),
)
], ],
); );
} }

View File

@ -42,8 +42,7 @@ class _RedeemProductState extends State<RedeemProduct> {
create: (context) => RedeemProductViewModel(), create: (context) => RedeemProductViewModel(),
child: Consumer<RedeemProductViewModel>( child: Consumer<RedeemProductViewModel>(
builder: (context, provider, child) { builder: (context, provider, child) {
double amount = provider.getAmount ?? provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!; amountController.text = NumberFormatter.numberCurrency(provider.getAmount!.toInt(), 'Rp ', 'id_ID', decimalDigits: 0);
amountController.text = NumberFormatter.numberCurrency(amount.toInt(), 'Rp ', 'id_ID', decimalDigits: 0);
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@ -76,8 +75,8 @@ class _RedeemProductState extends State<RedeemProduct> {
const SizedBox(height: 16), const SizedBox(height: 16),
segmentAmount( segmentAmount(
context, context,
provider.getAmount ?? provider.getCurrentProduct.priceUnit! * (provider.getCurrentProduct.totalUnit! / 2.0), provider.getAmount!,
provider.getUnit ?? provider.getCurrentProduct.totalUnit! / 2.0, provider.getUnit!,
(value) { (value) {
provider.setUnit(value); provider.setUnit(value);
}, },

View File

@ -61,6 +61,8 @@ class RedeemProductViewModel extends ChangeNotifier {
void setProduct(PortfolioProduct product) { void setProduct(PortfolioProduct product) {
currentProduct = product; currentProduct = product;
amount = product.priceUnit! * (product.totalUnit! / 2.0);
unit = (product.totalUnit! / 2.0);
notifyListeners(); notifyListeners();
} }

View File

@ -55,32 +55,43 @@ class SelectGoalInvesting extends StatelessWidget {
create: (context) => ProductViewModel(), create: (context) => ProductViewModel(),
child: Consumer<ProductViewModel>( child: Consumer<ProductViewModel>(
builder: (context, provider, child) { builder: (context, provider, child) {
return InputInvestmentView( return Padding(
selectedPlan: p0, padding: EdgeInsets.symmetric(vertical: 16),
nextMove: (value) { child: InputInvestmentView(
Navigator.pop(context); currentPlan: p0,
int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll(',', '')); changePlan: () {
showModalBottomSheet( Navigator.pop(context);
context: context, showModalBottomSheet(
isScrollControlled: true, context: context,
builder: (context) => isScrollControlled: true,
ChangeNotifierProvider( builder: (context) => SelectGoalInvesting(),
create: (context) => ProductViewModel(), );
child: Consumer<ProductViewModel>( },
builder: (context, provider, child) { nextMove: (value) {
return TotalPaymentView( Navigator.pop(context);
listProduct: [ int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll('.', ''));
provider.getSelectedProduct showModalBottomSheet(
], context: context,
totalInvest: formatIntParse, isScrollControlled: true,
isAgree: provider.isAgree, builder: (context) =>
onTapAgree: provider.setAgree, ChangeNotifierProvider(
); create: (context) => ProductViewModel(),
} child: Consumer<ProductViewModel>(
), builder: (context, provider, child) {
) return TotalPaymentView(
); listProduct: [
}, provider.getSelectedProduct
],
totalInvest: formatIntParse,
isAgree: provider.isAgree,
onTapAgree: provider.setAgree,
);
}
),
)
);
},
),
); );
} }
), ),

View File

@ -44,7 +44,7 @@ class DashboardPublicView extends StatelessWidget {
return Scaffold( return Scaffold(
body: SingleChildScrollView( body: SingleChildScrollView(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 32.0, top: 36.0,
bottom: 8.0, bottom: 8.0,
left: 24.0, left: 24.0,
right: 24.0, right: 24.0,
@ -59,6 +59,7 @@ class DashboardPublicView extends StatelessWidget {
image: PathAssets.icon1, image: PathAssets.icon1,
width: SizeConfig.width * .35, width: SizeConfig.width * .35,
), ),
SizedBox(height: SizeConfig.height * .03),
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
heightFactor: 1, heightFactor: 1,
@ -105,7 +106,7 @@ class DashboardPublicView extends StatelessWidget {
provider.loginGoogle(context); provider.loginGoogle(context);
}, },
), ),
SizedBox(height: SizeConfig.height * .15), SizedBox(height: SizeConfig.height * .07),
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [