diff --git a/assets/icons/icon-bag.png b/assets/icons/icon-bag.png new file mode 100644 index 0000000..6fc02db Binary files /dev/null and b/assets/icons/icon-bag.png differ diff --git a/assets/icons/icon-car.png b/assets/icons/icon-car.png new file mode 100644 index 0000000..f4f4708 Binary files /dev/null and b/assets/icons/icon-car.png differ diff --git a/assets/icons/icon-cart.png b/assets/icons/icon-cart.png new file mode 100644 index 0000000..429452d Binary files /dev/null and b/assets/icons/icon-cart.png differ diff --git a/assets/icons/icon-gadget.png b/assets/icons/icon-gadget.png new file mode 100644 index 0000000..5bfd007 Binary files /dev/null and b/assets/icons/icon-gadget.png differ diff --git a/assets/icons/icon-market.png b/assets/icons/icon-market.png new file mode 100644 index 0000000..5cb8e44 Binary files /dev/null and b/assets/icons/icon-market.png differ diff --git a/assets/icons/icon-plane.png b/assets/icons/icon-plane.png new file mode 100644 index 0000000..91a6a9c Binary files /dev/null and b/assets/icons/icon-plane.png differ diff --git a/assets/icons/icon-portofolio.png b/assets/icons/icon-portofolio.png new file mode 100644 index 0000000..b446ff0 Binary files /dev/null and b/assets/icons/icon-portofolio.png differ diff --git a/assets/icons/icon-thumb.png b/assets/icons/icon-thumb.png new file mode 100644 index 0000000..0bac76a Binary files /dev/null and b/assets/icons/icon-thumb.png differ diff --git a/assets/icons/icon-ticket.png b/assets/icons/icon-ticket.png new file mode 100644 index 0000000..036b360 Binary files /dev/null and b/assets/icons/icon-ticket.png differ diff --git a/assets/images/img-open-shopping.png b/assets/images/img-open-shopping.png new file mode 100644 index 0000000..8d76697 Binary files /dev/null and b/assets/images/img-open-shopping.png differ diff --git a/assets/images/img-payment-success.png b/assets/images/img-payment-success.png new file mode 100644 index 0000000..cac693d Binary files /dev/null and b/assets/images/img-payment-success.png differ diff --git a/lib/application/assets/path_assets.dart b/lib/application/assets/path_assets.dart index 770064a..2431f43 100644 --- a/lib/application/assets/path_assets.dart +++ b/lib/application/assets/path_assets.dart @@ -40,6 +40,15 @@ class PathAssets { static const String iconChecklistOutlined = 'assets/icons/icon-ceklis-outline.png'; static const String iconLock = 'assets/icons/icon-lock.png'; + static const String iconThumb = 'assets/icons/icon-thumb.png'; + static const String iconPortofolio = 'assets/icons/icon-portofolio.png'; + static const String iconPlane = 'assets/icons/icon-plane.png'; + static const String iconCart = 'assets/icons/icon-cart.png'; + static const String iconBag = 'assets/icons/icon-bag.png'; + static const String iconMarket = 'assets/icons/icon-market.png'; + static const String iconTicket = 'assets/icons/icon-ticket.png'; + static const String iconGadget = 'assets/icons/icon-gadget.png'; + static const String iconCar = 'assets/icons/icon-car.png'; /// IMAGE static const String imgSplashLogo = 'assets/images/splash-logo.png'; @@ -73,6 +82,8 @@ class PathAssets { static const String imgGuideBank = 'assets/images/img-guide-bank.png'; static const String imgGuide1 = 'assets/images/img-guide1.png'; static const String imgGuide2 = 'assets/images/img-guide2.png'; + static const String imgOpenShopping = 'assets/images/img-open-shopping.png'; + static const String imgPaymentSuccess = 'assets/images/img-payment-success.png'; static const String frameSignature = 'assets/images/frame-signature.png'; static const String imgFinish = 'assets/images/img-finish.png'; } diff --git a/lib/application/component/button/button_view.dart b/lib/application/component/button/button_view.dart index 888dc89..676dd89 100644 --- a/lib/application/component/button/button_view.dart +++ b/lib/application/component/button/button_view.dart @@ -9,7 +9,7 @@ class ButtonView extends StatelessWidget { final double? height, width, widthSuffix, widthPrefix, marginVertical; final EdgeInsetsGeometry? contentPadding; final bool isSecondaryColor, isOutlined, heightWrapContent, disabled; - final Color? backgroundColor, textColor, borderColor; + final Color? backgroundColor, textColor, borderColor, disabledBgColor; final MainAxisAlignment? mainAxisAlignmentContent; // final _widthBtn = SizeConfig.screenWidth / 1.5; final _widthBtn = SizeConfig.width * .9; @@ -34,6 +34,7 @@ class ButtonView extends StatelessWidget { this.backgroundColor, this.borderColor, this.textColor, + this.disabledBgColor, this.textWeight = FontWeight.bold, this.textSize, this.textAlign = TextAlign.center, @@ -65,7 +66,7 @@ class ButtonView extends StatelessWidget { height: heightWrapContent ? null : height ?? _heightBtn, child: ElevatedButton( style: ElevatedButton.styleFrom( - disabledBackgroundColor: isOutlined ? Colors.white : color.surface, + disabledBackgroundColor: disabledBgColor ?? (isOutlined ? Colors.white : color.surface), padding: contentPadding, backgroundColor: backgroundColor ?? (isOutlined diff --git a/lib/application/component/modal_redirect_app.dart b/lib/application/component/modal_redirect_app.dart new file mode 100644 index 0000000..3292e4d --- /dev/null +++ b/lib/application/component/modal_redirect_app.dart @@ -0,0 +1,41 @@ +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:flutter/material.dart'; + +class TypeApp { + String img, title; + + TypeApp(this.img, this.title); +} + +class ModalRedirectApp extends StatelessWidget { + final String value; + const ModalRedirectApp({super.key, required this.value}); + + @override + Widget build(BuildContext context) { + Map typeApp = { + 'Shopping Pay': TypeApp(PathAssets.imgOpenShopping, 'Shopping App'), + }; + + return Column( + children: [ + ImageView(image: typeApp[value]!.img), + Text('Open ${typeApp[value]!.title}', + style: TextStyle( + fontSize: 22, + fontWeight: FontWeight.w600, + color: ColorPalette.slate800 + ), + ), + Text('You will be redirected to the ${typeApp[value]!.title.toLowerCase()} to continue the payment', + style: TextStyle( + fontSize: 16, + color: ColorPalette.slate400 + ), + ) + ], + ); + } +} diff --git a/lib/application/component/numeric_pad/numeric_pad.dart b/lib/application/component/numeric_pad/numeric_pad.dart index 797aa82..959d666 100644 --- a/lib/application/component/numeric_pad/numeric_pad.dart +++ b/lib/application/component/numeric_pad/numeric_pad.dart @@ -36,6 +36,18 @@ class NumericPad extends StatelessWidget { ], ), 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( mainAxisAlignment: MainAxisAlignment.spaceEvenly, crossAxisAlignment: CrossAxisAlignment.center, @@ -54,7 +66,7 @@ class NumericPad extends StatelessWidget { Widget dividerGradient(bool isHorizontal, AlignmentGeometry gradientFrom, AlignmentGeometry gradientTo, {bool fullColor = false}) { return Container( width: isHorizontal ? SizeConfig.width : 1, - height: isHorizontal ? 1 : SizeConfig.height * 0.11, + height: isHorizontal ? 1 : SizeConfig.height * 0.097, decoration: BoxDecoration( gradient: LinearGradient( colors: [ @@ -100,9 +112,14 @@ class NumericPad extends StatelessWidget { Widget removeWidget() { return Expanded( - child: Icon( - Icons.highlight_remove, - size: 28, + child: GestureDetector( + onTap: () { + onNumberSelected(''); + }, + child: Icon( + Icons.highlight_remove, + size: 28, + ), ) ); } diff --git a/lib/application/component/risk_profile.dart b/lib/application/component/risk_profile.dart new file mode 100644 index 0000000..a2a0cf7 --- /dev/null +++ b/lib/application/component/risk_profile.dart @@ -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 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(), + ) + ], + ); + } +} diff --git a/lib/application/component/goal_investing_view.dart b/lib/application/component/subscribe/goal_investing_view.dart similarity index 65% rename from lib/application/component/goal_investing_view.dart rename to lib/application/component/subscribe/goal_investing_view.dart index 77f96fd..80c3e53 100644 --- a/lib/application/component/goal_investing_view.dart +++ b/lib/application/component/subscribe/goal_investing_view.dart @@ -1,5 +1,7 @@ import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/subscribe/other_plan_view.dart'; import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/route/route.dart'; import 'package:cims_apps/core/utils/size_config.dart'; import 'package:flutter/material.dart'; @@ -11,7 +13,8 @@ class GoalInvest { } class GoalInvestingView extends StatelessWidget { - const GoalInvestingView({super.key}); + final void Function(String) onListSelected; + const GoalInvestingView({super.key, required this.onListSelected}); @override Widget build(BuildContext context) { @@ -19,7 +22,7 @@ class GoalInvestingView extends StatelessWidget { GoalInvest(PathAssets.iconToga, 'Education'), GoalInvest(PathAssets.iconCake, 'Marriage'), GoalInvest(PathAssets.iconHouse, 'Old age days'), - GoalInvest(PathAssets.iconCreatePlan, 'Create Plan'), + GoalInvest(PathAssets.iconCreatePlan, 'Other Plan'), ]; return Column( @@ -28,6 +31,20 @@ class GoalInvestingView extends StatelessWidget { return Padding( padding: EdgeInsets.only(top: e.key != 0 ? 16 : 0), child: ListTile( + onTap: () { + if(e.value.title == 'Other Plan'){ + routePush( + context, + page: OtherPlanView( + selectedPlan: (value) { + onListSelected(e.value.title); + }, + ) + ); + }else{ + onListSelected(e.value.title); + } + }, shape: RoundedRectangleBorder( side: BorderSide(color: ColorPalette.slate200), borderRadius: BorderRadius.circular(14) @@ -47,10 +64,11 @@ class GoalInvestingView extends StatelessWidget { title: Text(e.value.title, style: TextStyle( fontWeight: FontWeight.w600, - fontSize: 16 + fontSize: 16, + color: ColorPalette.slate800 ), ), - trailing: Icon(Icons.chevron_right_rounded), + trailing: Icon(Icons.chevron_right_rounded, color: ColorPalette.slate400), ), ); }).toList() diff --git a/lib/application/component/subscribe/input_investment_view.dart b/lib/application/component/subscribe/input_investment_view.dart new file mode 100644 index 0000000..846a057 --- /dev/null +++ b/lib/application/component/subscribe/input_investment_view.dart @@ -0,0 +1,143 @@ +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/theme/color_palette.dart'; +import 'package:cims_apps/core/utils/number_formatter.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:flutter/material.dart'; + +class InputInvestmentView extends StatefulWidget { + final String selectedPlan; + final void Function(String value) nextMove; + const InputInvestmentView({super.key, required this.selectedPlan, required this.nextMove}); + + @override + State createState() => _InputInvestmentViewState(); +} + +class _InputInvestmentViewState extends State { + TextEditingController inputController = TextEditingController(); + + @override + void initState() { + // TODO: implement initState + inputController.text = 'Rp 0'; + super.initState(); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + inputController.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(16) + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + SizedBox(height: 16), + Padding( + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(widget.selectedPlan, + 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: () { + widget.nextMove(inputController.text); + }, + width: SizeConfig.width, + heightWrapContent: true, + contentPadding: EdgeInsets.symmetric(vertical: 16), + marginVertical: 0, + ) + ], + ), + ), + ], + ), + );; + } +} diff --git a/lib/application/component/subscribe/other_plan_view.dart b/lib/application/component/subscribe/other_plan_view.dart new file mode 100644 index 0000000..60bff7a --- /dev/null +++ b/lib/application/component/subscribe/other_plan_view.dart @@ -0,0 +1,182 @@ +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/custom_app_bar/custom_app_bar.dart'; +import 'package:cims_apps/application/component/image/image_view.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/size_config.dart'; +import 'package:flutter/material.dart'; + +class Plan { + String img, name; + + Plan(this.img, this.name); +} + +class OtherPlanView extends StatefulWidget { + final void Function(String value) selectedPlan; + const OtherPlanView({super.key, required this.selectedPlan}); + + @override + State createState() => _OtherPlanViewState(); +} + +class _OtherPlanViewState extends State { + TextEditingController createController = TextEditingController(); + Plan selectedPlan = Plan('', ''); + + List listPlan = [ + Plan(PathAssets.iconToga, 'Education'), + Plan(PathAssets.iconCake, 'Marriage'), + Plan(PathAssets.iconHouse, 'Home'), + Plan(PathAssets.iconTicket, 'Entertainment'), + Plan(PathAssets.iconGadget, 'Gadget'), + Plan(PathAssets.iconMarket, 'Business'), + Plan(PathAssets.iconBag, 'Fashion'), + Plan(PathAssets.iconCart, 'Shop'), + Plan(PathAssets.iconCar, 'Vehicle'), + Plan(PathAssets.iconPlane, 'Holiday'), + Plan(PathAssets.iconCreatePlan, 'Create Plan'), + ]; + + @override + void initState() { + // TODO: implement initState + super.initState(); + } + + @override + void dispose() { + // TODO: implement dispose + super.dispose(); + createController.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: CustomAppBar( + height: 70, + title: 'Other Plan' + ), + body: GridView( + padding: EdgeInsets.symmetric(horizontal: 24, vertical: 32), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3), + children: listPlan.map((e) => cardPlan(e)).toList(), + ), + bottomNavigationBar: Container( + height: 110, + padding: EdgeInsets.symmetric(horizontal: 24), + child: ButtonView( + name: 'Select', + disabled: !(selectedPlan.img != ''), + onPressed: () { + Navigator.pop(context); + widget.selectedPlan(selectedPlan.name); + }, + heightWrapContent: true, + width: SizeConfig.width, + contentPadding: EdgeInsets.symmetric(vertical: 12), + textColor: selectedPlan.img == '' ? ColorPalette.slate500 : ColorPalette.white, + disabledBgColor: ColorPalette.slate200, + backgroundColor: ColorPalette.primary, + ), + ), + ); + } + + Widget cardPlan(Plan plan) { + return GestureDetector( + onTap: () { + if(plan.name == 'Create Plan'){ + showModalBottomSheet( + context: context, + builder: (context) => modalCreatePlan(), + ); + } + setState(() { + selectedPlan = plan; + }); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + border: Border.all(color: selectedPlan == plan ? ColorPalette.primary : Colors.transparent) + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + width: SizeConfig.width * 0.12, + height: SizeConfig.width * 0.12, + padding: EdgeInsets.all(12), + alignment: Alignment.center, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: ColorPalette.blue200.withOpacity(0.5) + ), + child: ImageView(image: plan.img) + ), + SizedBox(height: 12), + Text(plan.name, + style: TextStyle( + color: selectedPlan == plan ? ColorPalette.primary : ColorPalette.slate800, + fontWeight: FontWeight.w600 + ), + ) + ], + ), + ), + ); + } + + Widget modalCreatePlan() { + return Container( + padding: EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Create your plan', + style: TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.slate800, + fontSize: 16 + ), + ), + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Icon(Icons.close_rounded), + ) + ], + ), + TextFormView( + name: 'Objective Name', + ctrl: createController, + ), + SizedBox(height: 24), + ButtonView( + name: 'Select', + marginVertical: 0, + disabled: !(createController.text != ''), + onPressed: () { + Navigator.of(context)..pop()..pop(); + widget.selectedPlan(selectedPlan.name); + }, + heightWrapContent: true, + width: SizeConfig.width, + contentPadding: EdgeInsets.symmetric(vertical: 16), + textColor: createController.text == '' ? ColorPalette.slate500 : ColorPalette.white, + disabledBgColor: ColorPalette.slate200, + backgroundColor: ColorPalette.primary, + ), + ], + ), + ); + } +} diff --git a/lib/application/component/subscribe/total_payment_view.dart b/lib/application/component/subscribe/total_payment_view.dart new file mode 100644 index 0000000..634be7f --- /dev/null +++ b/lib/application/component/subscribe/total_payment_view.dart @@ -0,0 +1,254 @@ +import 'package:cims_apps/application/component/button/button_view.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/route/route.dart'; +import 'package:cims_apps/core/utils/number_formatter.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view/step_subscribe/payment_method_view.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; +import 'package:flutter/material.dart'; + +class TotalPaymentView extends StatefulWidget { + final int totalInvest; + final List listProduct; + const TotalPaymentView({ + super.key, + required this.listProduct, + required this.totalInvest, + }); + + @override + State createState() => _TotalPaymentViewState(); +} + +class _TotalPaymentViewState extends State { + bool isAgreement = false; + + @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: [ + const Text('Your Investment Today', + style: TextStyle( + color: ColorPalette.slate800, + fontWeight: FontWeight.w600, + fontSize: 16 + ), + ), + GestureDetector( + onTap: () => Navigator.pop(context), + child: const Icon(Icons.close_rounded) + ) + ], + ), + ), + ...widget.listProduct.asMap().entries.map((e) { + return Container( + padding: const EdgeInsets.only(left: 16, right: 24, bottom: 16, top: 16), + decoration: BoxDecoration( + border: Border( + left: BorderSide( + width: 8, + color: ColorPalette.investTypeColor[e.value.type]! + ) + ) + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + flex: 5, + child: Text(e.value.name ?? '', + style: const TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w600, + fontSize: 16 + ), + ) + ), + Expanded( + flex: 7, + child: Text( + NumberFormatter.numberCurrency(widget.totalInvest * e.value.totalPercent!, 'Rp ', 'id_ID'), + textAlign: TextAlign.end, + style: const TextStyle( + fontWeight: FontWeight.w700, + fontSize: 18, + color: ColorPalette.slate800 + ), + ) + ) + ], + ), + ); + }).toList(), + const SizedBox(height: 16), + const Padding( + padding: 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 + ), + ) + ], + ), + ), + const SizedBox(height: 16,), + Container( + color: ColorPalette.slate200.withOpacity(0.5), + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('Total', + style: TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w600, + fontSize: 16 + ), + ), + Text(NumberFormatter.numberCurrency(widget.totalInvest, 'Rp ', 'id_ID'), + textAlign: TextAlign.end, + style: const 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: [ + const Text('Total Payment', + style: TextStyle( + color: ColorPalette.slate400, + fontWeight: FontWeight.w600, + fontSize: 16 + ), + ), + Text(NumberFormatter.numberCurrency(widget.totalInvest, 'Rp ', 'id_ID'), + textAlign: TextAlign.end, + style: const TextStyle( + fontWeight: FontWeight.w700, + fontSize: 18, + color: ColorPalette.slate800 + ), + ) + ], + ), + ), + ButtonView( + disabled: !isAgreement, + name: 'Subscribe Now', + onPressed: () { + routePush(context, page: PaymentMethodView( + totalInvest: widget.totalInvest, + )); + }, + disabledBgColor: ColorPalette.slate200.withOpacity(0.5), + textColor: isAgreement ? Colors.white : ColorPalette.slate400, + textWeight: FontWeight.w700, + textSize: 20, + backgroundColor: ColorPalette.primary, + ) + ], + ), + ); + } + + Widget buttonAgreement() { + bool isAgree = isAgreement; + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + setState(() { + isAgreement = !isAgreement; + }); + }, + child: AnimatedContainer( + margin: const EdgeInsets.only(top: 4), + duration: const Duration(milliseconds: 200), + height: 16, + width: 16, + padding: const EdgeInsets.all(1), + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: isAgree ? ColorPalette.primary : ColorPalette.slate200 + ) + ), + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + child: Container( + decoration: BoxDecoration( + color: isAgree ? ColorPalette.primary : ColorPalette.white, + shape: BoxShape.circle + ), + ), + ), + ), + ), + const SizedBox(width: 12,), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const 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: const Text('Read More', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + decoration: TextDecoration.underline, + color: ColorPalette.primary + ), + ) + ) + ], + ) + ) + ], + ), + ); + } +} \ No newline at end of file diff --git a/lib/application/component/success_view.dart b/lib/application/component/success_view.dart new file mode 100644 index 0000000..7541243 --- /dev/null +++ b/lib/application/component/success_view.dart @@ -0,0 +1,52 @@ +import 'package:cims_apps/application/component/button/button_view.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:flutter/material.dart'; + +class SuccessView extends StatelessWidget { + final String img; + final String textTitle; + final Widget? subtitle; + final void Function() nextRoute; + const SuccessView({super.key, + required this.img, + required this.textTitle, + required this.subtitle, + required this.nextRoute + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ImageView(image: img, width: SizeConfig.width * .8,), + Text(textTitle, + style: TextStyle( + fontWeight: FontWeight.w700, + fontSize: 28, + ), + ), + subtitle ?? SizedBox() + ], + ), + ), + bottomNavigationBar: Container( + padding: EdgeInsets.all(24), + height: 110, + width: SizeConfig.width, + child: ButtonView( + name: 'Done', + onPressed: nextRoute, + marginVertical: 0, + heightWrapContent: true, + width: SizeConfig.width, + textSize: 20, + contentPadding: EdgeInsets.symmetric(vertical: 12), + ), + ), + ); + } +} diff --git a/lib/application/component/text_form/text_form_view.dart b/lib/application/component/text_form/text_form_view.dart index d52d093..959fbcc 100644 --- a/lib/application/component/text_form/text_form_view.dart +++ b/lib/application/component/text_form/text_form_view.dart @@ -5,6 +5,7 @@ import 'package:remove_emoji_input_formatter/remove_emoji_input_formatter.dart'; class TextFormView extends StatelessWidget { final String name; + final double nameSize; final String? helperText; final String? initialValue; final VoidCallback? onTap; @@ -40,6 +41,7 @@ class TextFormView extends StatelessWidget { TextFormView( {Key? key, required this.name, + this.nameSize = 16, this.helperText, this.onTap, this.fontColorDisabled, @@ -97,17 +99,17 @@ class TextFormView extends StatelessWidget { children: [ Text( name, - style: const TextStyle( - fontSize: 16, + style: TextStyle( + fontSize: nameSize, fontWeight: FontWeight.w600, color: ColorPalette.slate800, ), ), suffixLable ?? - const Text( + Text( "", style: TextStyle( - fontSize: 16, + fontSize: nameSize, color: Colors.red, ), ), diff --git a/lib/application/theme/color_palette.dart b/lib/application/theme/color_palette.dart index 22711ea..992d6d5 100644 --- a/lib/application/theme/color_palette.dart +++ b/lib/application/theme/color_palette.dart @@ -75,6 +75,7 @@ class ColorPalette { static const Color backgroundBlueLight = Color(0xFFEBF3FD); static const Color blue50 = Color(0xFFEFF6FF); static const Color blue200 = Color(0xFFBFDBFE); + static const Color blue900 = Color(0xFF1E3A8A); static const Color slate50 = Color(0xFFF8FAFC); static const Color slate200 = Color(0xFFE2E8F0); static const Color slate300 = Color(0xFFCBD5E1); diff --git a/lib/features/auth/login/view/password_view.dart b/lib/features/auth/login/view/password_view.dart index a5ea828..5f6906a 100644 --- a/lib/features/auth/login/view/password_view.dart +++ b/lib/features/auth/login/view/password_view.dart @@ -65,6 +65,7 @@ class PasswordView extends StatelessWidget { name: 'Confirm', heightWrapContent: true, width: SizeConfig.width, + textSize: 18, marginVertical: 0, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), onPressed: nextStep, diff --git a/lib/features/bottom_navigation_view.dart b/lib/features/bottom_navigation_view.dart index ffe57f5..fdd1da2 100644 --- a/lib/features/bottom_navigation_view.dart +++ b/lib/features/bottom_navigation_view.dart @@ -1,8 +1,10 @@ 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/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:flutter/material.dart'; +import 'package:provider/provider.dart'; class BottomNavigationView extends StatefulWidget { const BottomNavigationView({Key? key}) : super(key: key); diff --git a/lib/features/dashboard/dashboard_account/view/homepage/homepage_view.dart b/lib/features/dashboard/dashboard_account/view/homepage/homepage_view.dart index 0e81a6d..463669b 100644 --- a/lib/features/dashboard/dashboard_account/view/homepage/homepage_view.dart +++ b/lib/features/dashboard/dashboard_account/view/homepage/homepage_view.dart @@ -228,7 +228,7 @@ class _HomeViewState extends State { children: listPortofolioType.asMap().entries.map((e) { return GestureDetector( onTap: () { - routePush(context, page: InvestTypeView(e.value.name)); + routePush(context, page: InvestTypeView(title: e.value.name)); }, child: Container( color: Colors.white, diff --git a/lib/features/dashboard/dashboard_account/view/invest_type/invest_type_view.dart b/lib/features/dashboard/dashboard_account/view/invest_type/invest_type_view.dart index 0ea8805..ea362ab 100644 --- a/lib/features/dashboard/dashboard_account/view/invest_type/invest_type_view.dart +++ b/lib/features/dashboard/dashboard_account/view/invest_type/invest_type_view.dart @@ -6,21 +6,14 @@ import 'package:cims_apps/application/theme/color_palette.dart'; import 'package:cims_apps/core/route/route.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/product/product_view.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view/product_view.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; import 'package:flutter/material.dart'; - -class Product { - String name; - double yield; - double priceUnit; - double funds; - - Product(this.name, this.yield, this.priceUnit, this.funds); -} +import 'package:provider/provider.dart'; class InvestTypeView extends StatefulWidget { final String title; - const InvestTypeView(this.title, {super.key}); + const InvestTypeView({super.key, required this.title}); @override State createState() => _InvestTypeViewState(); @@ -29,71 +22,89 @@ class InvestTypeView extends StatefulWidget { class _InvestTypeViewState extends State { List listProduct = [ - Product('Gemilang Dana Kas Maxima', 8.17, 2600.79, 6300000), - Product('Gemilang Dana Likuid', 6.42, 1600.79, 2340000), - Product('Gemilang Income Fund', 8.17, 2600.79, 6300000) + Product(name: 'Gemilang Dana Kas Maxima', type: '', yield: 8.17, priceUnit: 2600.79, funds: 6300000), + Product(name: 'Gemilang Dana Likuid', type: '', yield: 6.42, priceUnit: 1600.79, funds: 2340000), + Product(name: 'Gemilang Income Fund', type: '', yield: 8.17, priceUnit: 2600.79, funds: 6300000) ]; + + @override + void initState() { + // TODO: implement initState + super.initState(); + listProduct = listProduct.map((e) { + e.type = widget.title; + return e; + }).toList(); + } @override Widget build(BuildContext context) { - return Scaffold( - body: SizedBox( - width: SizeConfig.width, - height: SizeConfig.height, - child: Stack( - children: [ - const ImageView(image: PathAssets.imgDashboardAccount), - Column( - children: [ - const SizedBox( - height: 50, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 24), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + return ChangeNotifierProvider( + create: (context) => ProductViewModel(), + child: Consumer( + builder: (context, provider, child) { + return Scaffold( + body: SizedBox( + width: SizeConfig.width, + height: SizeConfig.height, + child: Stack( + children: [ + const ImageView(image: PathAssets.imgDashboardAccount), + Column( children: [ - const BackButtonView(), - TextTitle(title: widget.title, color: Colors.white), - SizedBox( - width: SizeConfig.width * 0.1, - ) + const SizedBox( + height: 50, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const BackButtonView(), + TextTitle(title: widget.title, color: Colors.white), + SizedBox( + width: SizeConfig.width * 0.1, + ) + ], + ), + ), + const SizedBox( + height: 24, + ), + Container( + padding: const EdgeInsets.all(24), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: Colors.white + ), + child: Column( + children: [ + filters(), + ListView( + shrinkWrap: true, + children: listProduct.asMap().entries.map((e) { + return GestureDetector( + onTap: () { + provider.setSelectedProduct(e.value); + routePush(context, page: ProductView(widget.title)); + }, + child: Padding( + padding: EdgeInsets.only(top: e.key != 0 ? 24 : 0), + child: cardProduct(e.value), + ), + ); + }).toList(), + ) + ], + ), + ), ], - ), - ), - const SizedBox( - height: 24, - ), - Container( - padding: const EdgeInsets.all(24), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: Colors.white - ), - child: Column( - children: [ - filters(), - ListView( - shrinkWrap: true, - children: listProduct.asMap().entries.map((e) { - return GestureDetector( - onTap: () { - routePush(context, page: ProductView(widget.title)); - }, - child: Padding( - padding: EdgeInsets.only(top: e.key != 0 ? 24 : 0), - child: cardProduct(e.value), - ), - ); - }).toList(), - ) - ], - ), - ), - ], - ) - ], - ), + ) + ], + ), + ), + ); + } ), ); } @@ -158,7 +169,7 @@ class _InvestTypeViewState extends State { ), Expanded( child: Text( - product.name, + product.name ?? '', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 18 diff --git a/lib/features/dashboard/dashboard_account/view/plan/plan_view.dart b/lib/features/dashboard/dashboard_account/view/plan/plan_view.dart deleted file mode 100644 index 004462f..0000000 --- a/lib/features/dashboard/dashboard_account/view/plan/plan_view.dart +++ /dev/null @@ -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() - ], - ), - ), - ); - } -} diff --git a/lib/features/dashboard/dashboard_account/view/plan/view/plan_view.dart b/lib/features/dashboard/dashboard_account/view/plan/view/plan_view.dart new file mode 100644 index 0000000..664a044 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/plan/view/plan_view.dart @@ -0,0 +1,126 @@ +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/subscribe/goal_investing_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/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 createState() => _PlanViewState(); +} + +class _PlanViewState extends State { + TextEditingController inputController = TextEditingController(); + + @override + void initState() { + // TODO: implement initState + inputController.text = 'Rp 0'; + super.initState(); + } + + @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), + InputInvestmentView( + selectedPlan: text, + nextMove: (value) { + Navigator.pop(context); + int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll(',', '')); + showModalBottomSheet(context: context, builder: (context) => OptionsStartingInvest(totalInvest: formatIntParse)); + }, + ), + ], + ), + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/options_starting_invest.dart b/lib/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/options_starting_invest.dart new file mode 100644 index 0000000..1047ecd --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/options_starting_invest.dart @@ -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 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,) + ], + ), + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/result_options_product.dart b/lib/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/result_options_product.dart new file mode 100644 index 0000000..ad54b8c --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/result_options_product.dart @@ -0,0 +1,153 @@ +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/component/subscribe/total_payment_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/product/view_model/product_view_model.dart'; +import 'package:flutter/material.dart'; + +class ResultOptionsProduct extends StatelessWidget { + final int totalInvest; + const ResultOptionsProduct({super.key, required this.totalInvest}); + + @override + Widget build(BuildContext context) { + List listProduct = [ + Product(name: 'Gemilang Dana Kas Maxima', type: 'Money Market', totalPercent: 0.7), + Product(name: 'Gemilang Dana Likuid', type: 'Bonds', totalPercent: 0.2), + Product(name: 'Gemilang Kas 2 Kelas A', type: 'Shares', totalPercent: 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 + ), + ), + 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, + fontSize: 12 + ), + ), + ) + ], + ), + ), + 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: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => + TotalPaymentView( + listProduct: listProduct, + totalInvest: totalInvest, + ) + ); + }, + width: SizeConfig.width, + heightWrapContent: true, + contentPadding: const EdgeInsets.symmetric(vertical: 16), + marginVertical: 0, + ) + ], + ), + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart b/lib/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart new file mode 100644 index 0000000..2409e63 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart @@ -0,0 +1,6 @@ +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; +import 'package:flutter/material.dart'; + +class PlanViewModel extends ChangeNotifier { + List listProduct = []; +} \ No newline at end of file diff --git a/lib/features/dashboard/dashboard_account/view/product/product_chart_view.dart b/lib/features/dashboard/dashboard_account/view/product/view/product_chart_view.dart similarity index 100% rename from lib/features/dashboard/dashboard_account/view/product/product_chart_view.dart rename to lib/features/dashboard/dashboard_account/view/product/view/product_chart_view.dart diff --git a/lib/features/dashboard/dashboard_account/view/product/product_view.dart b/lib/features/dashboard/dashboard_account/view/product/view/product_view.dart similarity index 90% rename from lib/features/dashboard/dashboard_account/view/product/product_view.dart rename to lib/features/dashboard/dashboard_account/view/product/view/product_view.dart index 3c30642..40438e1 100644 --- a/lib/features/dashboard/dashboard_account/view/product/product_view.dart +++ b/lib/features/dashboard/dashboard_account/view/product/view/product_view.dart @@ -9,9 +9,12 @@ import 'package:cims_apps/application/component/text_title/text_title.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/product/product_chart_view.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view/product_chart_view.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view/step_subscribe/select_goal_investing.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; import 'package:flutter/material.dart'; import 'package:group_button/group_button.dart'; +import 'package:provider/provider.dart'; class Time { int value; @@ -97,58 +100,65 @@ class _ProductViewState extends State { @override Widget build(BuildContext context) { - return Scaffold( - body: SizedBox( - child: Stack( - children: [ - const ImageView(image: PathAssets.imgDashboardAccount), - Column( - children: [ - const SizedBox( - height: 50, - ), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 24), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - BackButtonView(), - Wrap( - spacing: 12, - children: [ - Icon(Icons.search_rounded, color: ColorPalette.blue200, size: 32), - Icon(Icons.file_download_outlined, color: ColorPalette.blue200, size: 32), - Icon(Icons.star_outline_rounded, color: ColorPalette.blue200, size: 32) - ], - ) - ], + return ChangeNotifierProvider( + create: (context) => ProductViewModel(), + child: Scaffold( + body: SizedBox( + child: Stack( + children: [ + const ImageView(image: PathAssets.imgDashboardAccount), + Column( + children: [ + const SizedBox( + height: 50, ), - ), - const SizedBox( - height: 24, - ), - headContainer(), - const SizedBox( - height: 24, - ), - Expanded( - child: contentContainer() - ) - ], - ) - ], + const Padding( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + BackButtonView(), + Wrap( + spacing: 12, + children: [ + Icon(Icons.search_rounded, color: ColorPalette.blue200, size: 32), + Icon(Icons.file_download_outlined, color: ColorPalette.blue200, size: 32), + Icon(Icons.star_outline_rounded, color: ColorPalette.blue200, size: 32) + ], + ) + ], + ), + ), + const SizedBox( + height: 24, + ), + headContainer(), + const SizedBox( + height: 24, + ), + Expanded( + child: contentContainer() + ) + ], + ) + ], + ), ), - ), - bottomNavigationBar: Container( - height: SizeConfig.height * .1, - padding: const EdgeInsets.symmetric(horizontal: 24), - child: ButtonView( - name: 'Buy', - onPressed: () { - - }, - height: SizeConfig.height * 0.06, - marginVertical: 16, + bottomNavigationBar: Container( + height: SizeConfig.height * .1, + padding: const EdgeInsets.symmetric(horizontal: 24), + child: ButtonView( + name: 'Subscribe', + onPressed: () { + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => SelectGoalInvesting(), + ); + }, + height: SizeConfig.height * 0.06, + marginVertical: 16, + ), ), ), ); @@ -581,18 +591,10 @@ class _ProductViewState extends State { }, selectedMachineType ), - const Padding( - padding: EdgeInsets.only(top: 16, bottom: 8), - child: Text( - "Today's Investment Estimated Value", - style: TextStyle( - fontWeight: FontWeight.w600, - color: ColorPalette.slate800 - ), - ), - ), + SizedBox(height: 16), TextFormView( - name: '', + name: "Today's Investment Estimated Value", + nameSize: 14, ctrl: machineController, keyboardType: TextInputType.number, contentPadding: EdgeInsets.all(12), diff --git a/lib/features/dashboard/dashboard_account/view/product/view/step_subscribe/payment_method_view.dart b/lib/features/dashboard/dashboard_account/view/product/view/step_subscribe/payment_method_view.dart new file mode 100644 index 0000000..5b93729 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/product/view/step_subscribe/payment_method_view.dart @@ -0,0 +1,282 @@ +import 'dart:ffi'; + +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/custom_app_bar/custom_app_bar.dart'; +import 'package:cims_apps/application/component/modal_redirect_app.dart'; +import 'package:cims_apps/application/component/success_view.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/route/route.dart'; +import 'package:cims_apps/core/utils/number_formatter.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:cims_apps/features/bottom_navigation_view.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class Payment { + String name; + int fee; + double? discount; + + Payment({required this.name, required this.fee, this.discount = 1}); +} + +class PaymentMethodView extends StatefulWidget { + final int totalInvest; + const PaymentMethodView({super.key, required this.totalInvest }); + + @override + State createState() => _PaymentMethodViewState(); +} + +class _PaymentMethodViewState extends State { + int selectedPayment = 0; + Payment currentPayment = Payment(name: 'Wallet Pay', fee: 2500); + List listInstantPayment = [ + Payment(name: 'Wallet Pay', fee: 2500), + Payment(name: 'Link Start', fee: 2500), + Payment(name: 'Shopping Pay', fee: 2500, discount: 0.5), + Payment(name: 'Fund', fee: 2500, discount: 0.2) + ]; + + List listTransferBank = [ + Payment(name: 'BCA', fee: 2500), + Payment(name: 'Bank Republik Indonesia', fee: 2500) + ]; + + @override + Widget build(BuildContext context) { + + return Scaffold( + appBar: const CustomAppBar( + height: 70, + title: 'Payment Method' + ), + body: SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8), + child: Column( + children: [ + segmentTitle(Icons.bolt_rounded, 'Instant Payment'), + Wrap( + runSpacing: 16, + children: listInstantPayment.asMap().entries.map((e) { + return cardPayment(e.value, e.key); + }).toList(), + ), + const SizedBox(height: 8), + segmentTitle(Icons.account_balance_outlined, 'Transfer Bank'), + Wrap( + runSpacing: 16, + children: listTransferBank.asMap().entries.map((e) { + return cardPayment(e.value, e.key + (listInstantPayment.length)); + }).toList(), + ), + SizedBox(height: 16,) + ], + ), + ), + bottomNavigationBar: Container( + padding: const EdgeInsets.all(24), + child: segmentBottom(currentPayment) + ), + ); + } + + Widget segmentTitle(IconData? icon, String title) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Row( + children: [ + Icon(icon, color: ColorPalette.primary), + const SizedBox(width: 8), + Text(title, + style: const TextStyle( + color: ColorPalette.slate800, + fontWeight: FontWeight.w600, + fontSize: 16 + ), + ) + ], + ), + ); + } + + Widget cardPayment(Payment value, int index) { + return GestureDetector( + onTap: () { + setState(() { + selectedPayment = index; + currentPayment = value; + }); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(14), + border: Border.all(color: selectedPayment == index ? ColorPalette.primary : ColorPalette.slate200) + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(value.name, + style: const TextStyle( + fontSize: 16 + ), + ), + RichText( + text: TextSpan( + text: 'Payment fee ', + style: const TextStyle( + fontFamily: 'Manrope', + color: ColorPalette.slate400, + ), + children: [ + TextSpan( + text: '${NumberFormatter.numberCurrency(value.fee, 'Rp ', 'id_ID', decimalDigits: 0)} ', + style: TextStyle( + decoration: value.discount != 1 ? TextDecoration.lineThrough : TextDecoration.none, + fontFamily: 'Manrope', + color: ColorPalette.slate400, + ), + ), + if(value.discount != 1)...[ + TextSpan( + text: value.discount == 0 ? 'Free' : NumberFormatter.numberCurrency(value.fee * value.discount!, 'Rp ', 'id_ID', decimalDigits: 0), + style: const TextStyle( + fontFamily: 'Manrope', + color: ColorPalette.primary, + ), + ) + ] + ] + ) + ) + ], + ) + ], + ), + AnimatedContainer( + duration: const Duration(milliseconds: 200), + width: 16, + height: 16, + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: selectedPayment == index ? ColorPalette.primary : Colors.white, + border: Border.all(color: selectedPayment == index ? ColorPalette.primary : ColorPalette.slate200, width: 1.5) + ), + child: const Icon(Icons.done_rounded, color: Colors.white, size: 12), + ) + ], + ) + ], + ), + ), + ); + } + + Widget segmentBottom(Payment currentPayment) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + rowTotal('Total Invest', widget.totalInvest), + rowTotal('Payment Fee', (currentPayment.fee * currentPayment.discount!).toInt()), + Row( + children: [ + Expanded(child: Divider(color: ColorPalette.slate200, height: 1,)), + Icon(Icons.add, size: 10,) + ], + ), + rowTotal('Total Payment', widget.totalInvest + (currentPayment.fee * currentPayment.discount!).toInt()), + SizedBox(height: 24), + ButtonView( + name: 'Buy', + textSize: 18, + marginVertical: 0, + width: SizeConfig.width, + heightWrapContent: true, + contentPadding: EdgeInsets.symmetric(vertical: 16), + onPressed: () { + List redirect = ['Shopping Pay']; + if(redirect.contains(currentPayment.name)){ + showModalBottomSheet( + context: context, + builder: (context) { + return ModalRedirectApp(value: currentPayment.name); + }, + ); + } + routePush(context, page: SuccessView( + img: PathAssets.imgPaymentSuccess, + textTitle: 'Payment Success!', + subtitle: RichText( + text: TextSpan( + text: 'Payment With ', + style: TextStyle( + fontFamily: 'Manrope', + fontWeight: FontWeight.w600, + fontSize: 16, + color: ColorPalette.slate500 + ), + children: [ + TextSpan( + text: currentPayment.name, + style: TextStyle( + fontFamily: 'Manrope', + fontWeight: FontWeight.w700, + fontSize: 16, + color: ColorPalette.slate800 + ) + ), + TextSpan( + text: ' successful', + style: TextStyle( + fontFamily: 'Manrope', + fontWeight: FontWeight.w600, + fontSize: 16, + color: ColorPalette.slate500 + ) + ) + ] + ) + ), + nextRoute: () { + routePush(context, page: BottomNavigationView(), routeType: RouteType.pushRemove); + }, + )); + }, + ) + ], + ); + } + + Widget rowTotal(String title, int value) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(title, + style: const TextStyle( + fontWeight: FontWeight.w600, + color: ColorPalette.slate500, + fontSize: 16 + ), + ), + Text(NumberFormatter.numberCurrency(value, 'Rp', 'id_ID', decimalDigits: 0), + style: const TextStyle( + fontWeight: FontWeight.w700, + color: ColorPalette.slate800, + fontSize: 16 + ), + ) + ], + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/product/view/step_subscribe/select_goal_investing.dart b/lib/features/dashboard/dashboard_account/view/product/view/step_subscribe/select_goal_investing.dart new file mode 100644 index 0000000..5b3d081 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/product/view/step_subscribe/select_goal_investing.dart @@ -0,0 +1,83 @@ +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/total_payment_view.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; + +class SelectGoalInvesting extends StatelessWidget { + const SelectGoalInvesting({super.key}); + + @override + Widget build(BuildContext context) { + return MultiProvider( + providers: [ + ChangeNotifierProvider(create: (context) => ProductViewModel(),) + ], + child: Consumer( + builder: (context, provider, child) { + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16) + ), + padding: EdgeInsets.all(24), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Your Goal in Investing', + style: TextStyle( + fontWeight: FontWeight.w700, + color: ColorPalette.slate800, + fontSize: 18 + ), + ), + GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Icon(Icons.close_rounded, color: ColorPalette.slate400) + ) + ], + ), + SizedBox(height: 32), + GoalInvestingView( + onListSelected: (p0) { + Navigator.pop(context); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + return InputInvestmentView( + selectedPlan: p0, + nextMove: (value) { + Navigator.pop(context); + int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll(',', '')); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) => + TotalPaymentView( + listProduct: [ + provider.getSelectedProduct + ], + totalInvest: formatIntParse, + ) + ); + }, + ); + }, + ); + }, + ) + ], + ), + ); + } + ), + ); + } +} diff --git a/lib/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart b/lib/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart new file mode 100644 index 0000000..e7fddea --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart @@ -0,0 +1,27 @@ + +import 'package:flutter/material.dart'; + +class Product { + String? name, type; + double? yield; + double? priceUnit, funds, totalPercent; + + Product({this.name, this.type, this.yield, this.priceUnit, this.funds, this.totalPercent = 1}); +} + +class ProductViewModel extends ChangeNotifier { + static Product selectedProduct = Product(); + Product get getSelectedProduct => selectedProduct; + + double totalInvestment = 0; + + void setSelectedProduct(Product product) { + selectedProduct = product; + notifyListeners(); + } + + void setTotalInvestment(double value) { + totalInvestment = value; + notifyListeners(); + } +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index c94e8a0..70c5712 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -76,6 +76,10 @@ class MyApp extends StatelessWidget { secondary: const Color(0xFFFECDA6), onBackground: const Color(0xFFA9A9A9), ), + bottomSheetTheme: BottomSheetThemeData( + backgroundColor: Colors.white, + surfaceTintColor: Colors.white + ) // useMaterial3: true, ), initialRoute: initialRoute,