feat: plan view, wip plan view model

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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