feat: product chart view
This commit is contained in:
parent
d82d427bcc
commit
e1cabe0a09
|
@ -73,9 +73,11 @@ class ColorPalette {
|
|||
static const Color chathamsBlue = Color(0xFF285BB9);
|
||||
static const Color background = Color(0xFFDADADA);
|
||||
static const Color backgroundBlueLight = Color(0xFFEBF3FD);
|
||||
static const Color blue50 = Color(0xFFEFF6FF);
|
||||
static const Color blue200 = Color(0xFFBFDBFE);
|
||||
static const Color slate50 = Color(0xFFF8FAFC);
|
||||
static const Color slate200 = Color(0xFFE2E8F0);
|
||||
static const Color slate300 = Color(0xFFCBD5E1);
|
||||
static const Color slate400 = Color(0xFF94A3B8);
|
||||
static const Color slate500 = Color(0xFF64748B);
|
||||
static const Color slate800 = Color(0xFF1E293B);
|
||||
|
|
3
lib/core/utils/date_formatter.dart
Normal file
3
lib/core/utils/date_formatter.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
class DateFormatter {
|
||||
|
||||
}
|
19
lib/core/utils/number_formatter.dart
Normal file
19
lib/core/utils/number_formatter.dart
Normal file
|
@ -0,0 +1,19 @@
|
|||
import 'package:intl/intl.dart';
|
||||
|
||||
class NumberFormatter {
|
||||
NumberFormatter._();
|
||||
|
||||
static String numberCurrency(dynamic value, String symbol, String locale, {int? decimalDigits = 0}) {
|
||||
NumberFormat numberFormat = NumberFormat.currency(locale: locale, symbol: symbol, decimalDigits: decimalDigits);
|
||||
String formatValue = numberFormat.format(value);
|
||||
|
||||
return formatValue;
|
||||
}
|
||||
|
||||
static compactCurrency(dynamic value, String symbol, String locale) {
|
||||
NumberFormat numberFormat = NumberFormat.compactCurrency(locale: locale, symbol: symbol);
|
||||
String formatValue = numberFormat.format(value);
|
||||
|
||||
return formatValue;
|
||||
}
|
||||
}
|
|
@ -334,7 +334,7 @@ class _HomeViewState extends State<HomeView> {
|
|||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xffEFF6FF),
|
||||
color: ColorPalette.blue50,
|
||||
borderRadius: BorderRadius.circular(12)
|
||||
),
|
||||
child: const Column(
|
||||
|
|
|
@ -0,0 +1,325 @@
|
|||
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:fl_chart/fl_chart.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'dart:math';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
class ProductChartView extends StatefulWidget {
|
||||
final String tabType;
|
||||
final String lastTime;
|
||||
final List<double> dataChart;
|
||||
|
||||
const ProductChartView({
|
||||
super.key,
|
||||
required this.tabType,
|
||||
required this.lastTime,
|
||||
required this.dataChart,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ProductChartView> createState() => _ProductChartViewState();
|
||||
}
|
||||
|
||||
class _ProductChartViewState extends State<ProductChartView> {
|
||||
bool isShowLabel = true;
|
||||
DateTime dateTime = DateTime.now();
|
||||
double currentPrice = 0;
|
||||
int spotIndicator = 0;
|
||||
List<int> showingTooltipOnSpots = [1, 3, 5];
|
||||
List<FlSpot> spots = [];
|
||||
|
||||
double dataHighest = 0;
|
||||
double dataLowest = 0;
|
||||
|
||||
int indexHighestValue = 0;
|
||||
int indexLowestValue = 0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
spots = widget.dataChart.asMap().entries.map((e) {
|
||||
return FlSpot(double.parse(e.key.toString()), e.value);
|
||||
}).toList();
|
||||
dataHighest = widget.dataChart.reduce(max);
|
||||
dataLowest = widget.dataChart.reduce(min);
|
||||
currentPrice = dataHighest;
|
||||
indexHighestValue = widget.dataChart.indexWhere((element) => element == dataHighest);
|
||||
indexLowestValue = widget.dataChart.indexWhere((element) => element == dataLowest);
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant ProductChartView oldWidget) {
|
||||
// TODO: implement didUpdateWidget
|
||||
spots = widget.dataChart.asMap().entries.map((e) {
|
||||
return FlSpot(double.parse(e.key.toString()), e.value);
|
||||
}).toList();
|
||||
dataHighest = widget.dataChart.reduce(max);
|
||||
dataLowest = widget.dataChart.reduce(min);
|
||||
currentPrice = dataHighest;
|
||||
indexHighestValue = widget.dataChart.indexWhere((element) => element == dataHighest);
|
||||
indexLowestValue = widget.dataChart.indexWhere((element) => element == dataLowest);
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(
|
||||
widget.tabType,
|
||||
style: TextStyle(
|
||||
color: ColorPalette.slate400,
|
||||
fontWeight: FontWeight.w700
|
||||
),
|
||||
),
|
||||
Text(
|
||||
DateFormat('dd MMM yyyy').format(dateTime),
|
||||
style: TextStyle(
|
||||
color: ColorPalette.slate300
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: TextTitle(title: NumberFormatter.numberCurrency(currentPrice, 'Rp ', 'id_ID')),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
spacing: 8,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.trending_up_outlined,
|
||||
size: 18,
|
||||
color: ColorPalette.green500,
|
||||
),
|
||||
Text(
|
||||
'Rp20,30 (+3,88%)',
|
||||
style: TextStyle(
|
||||
color: ColorPalette.green500,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Last ${widget.lastTime}',
|
||||
style: TextStyle(
|
||||
color: ColorPalette.slate300,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 48),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 2.5,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
return LineChart(
|
||||
LineChartData(
|
||||
// showingTooltipIndicators: showingTooltipOnSpots.map((index) {
|
||||
// return ShowingTooltipIndicators([
|
||||
// LineBarSpot(
|
||||
// tooltipsOnBar,
|
||||
// lineBarsData.indexOf(tooltipsOnBar),
|
||||
// tooltipsOnBar.spots[index],
|
||||
// ),
|
||||
// ]);
|
||||
// }).toList(),
|
||||
extraLinesData: ExtraLinesData(
|
||||
verticalLines: [
|
||||
VerticalLine(
|
||||
color: Colors.transparent,
|
||||
x: indexHighestValue.toDouble(),
|
||||
label: VerticalLineLabel(
|
||||
show: spots.length < 7 ? false : isShowLabel,
|
||||
padding: EdgeInsets.only(bottom: 20, top: -25),
|
||||
alignment: (indexHighestValue / widget.dataChart.length) < 0.25 ? Alignment.topRight : (indexHighestValue / widget.dataChart.length) > 0.75 ? Alignment.topLeft : Alignment.topCenter,
|
||||
style: const TextStyle(
|
||||
color: ColorPalette.slate400,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
labelResolver: (p0) => NumberFormatter.numberCurrency(dataHighest, 'Rp ', 'id_ID'),
|
||||
)
|
||||
),
|
||||
VerticalLine(
|
||||
color: Colors.transparent,
|
||||
x: indexLowestValue.toDouble(),
|
||||
label: VerticalLineLabel(
|
||||
show: spots.length < 7 ? false : isShowLabel,
|
||||
padding: EdgeInsets.only(bottom: -5, top: 20),
|
||||
alignment: (indexLowestValue / widget.dataChart.length ) < 0.25 ? Alignment.bottomRight : (indexLowestValue / widget.dataChart.length ) > 0.75 ? Alignment.bottomLeft : Alignment.bottomCenter,
|
||||
style: const TextStyle(
|
||||
color: ColorPalette.slate400,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
labelResolver: (p0) => NumberFormatter.numberCurrency(dataLowest, 'Rp ', 'id_ID'),
|
||||
)
|
||||
)
|
||||
]
|
||||
),
|
||||
lineTouchData: LineTouchData(
|
||||
getTouchLineEnd: (barData, spotIndex) => double.infinity,
|
||||
getTouchedSpotIndicator: (barData, spotIndexes) {
|
||||
return spotIndexes.map((spotIndex) {
|
||||
return TouchedSpotIndicatorData(
|
||||
const FlLine(strokeWidth: 1, color: ColorPalette.primary),
|
||||
FlDotData(
|
||||
getDotPainter: (spot, percent, barData, index) =>
|
||||
FlDotCirclePainter(
|
||||
radius: 1,
|
||||
color: ColorPalette.white,
|
||||
strokeColor: ColorPalette.primary,
|
||||
strokeWidth: 2,
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
touchCallback: (FlTouchEvent event, LineTouchResponse? response) {
|
||||
if (response == null || response.lineBarSpots == null) {
|
||||
setState(() {
|
||||
isShowLabel = true;
|
||||
currentPrice = widget.dataChart.last;
|
||||
dateTime = DateTime.now();
|
||||
});
|
||||
return;
|
||||
}
|
||||
double? spotIndex = response.lineBarSpots?[0].x;
|
||||
if(spotIndex != null){
|
||||
int absIndex = (spotIndex.toInt() - (spots.length - 1)).abs();
|
||||
setState(() {
|
||||
isShowLabel = false;
|
||||
spotIndicator = spotIndex.toInt();
|
||||
currentPrice = widget.dataChart[spotIndex.toInt()];
|
||||
dateTime = DateTime.now().subtract(Duration(days: absIndex));
|
||||
});
|
||||
}
|
||||
},
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
tooltipBgColor: Colors.transparent,
|
||||
tooltipPadding: EdgeInsets.all(0),
|
||||
fitInsideHorizontally: true,
|
||||
showOnTopOfTheChartBoxArea: true,
|
||||
getTooltipItems: (touchedSpots) {
|
||||
return touchedSpots.map((LineBarSpot touchedSpot) {
|
||||
final textStyle = TextStyle(
|
||||
color: ColorPalette.slate500,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 12,
|
||||
);
|
||||
return LineTooltipItem(
|
||||
DateFormat('dd MMM yyyy').format(dateTime),
|
||||
textStyle,
|
||||
);
|
||||
}).toList();
|
||||
},
|
||||
),
|
||||
handleBuiltInTouches: true,
|
||||
),
|
||||
lineBarsData: [
|
||||
LineChartBarData(
|
||||
color: ColorPalette.primary,
|
||||
spots: spots,
|
||||
isCurved: true,
|
||||
preventCurveOverShooting: true,
|
||||
isStrokeCapRound: true,
|
||||
isStrokeJoinRound: true,
|
||||
barWidth: 1.2,
|
||||
belowBarData: BarAreaData(
|
||||
show: true,
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
Color(0xFF5B8FF9),
|
||||
Colors.white
|
||||
]
|
||||
),
|
||||
spotsLine: BarAreaSpotsLine(
|
||||
show: true,
|
||||
applyCutOffY: true,
|
||||
flLineStyle: FlLine(
|
||||
color: ColorPalette.primary,
|
||||
strokeWidth: 1,
|
||||
),
|
||||
checkToShowSpotLine: (spot) {
|
||||
if(spot.x == spotIndicator && !isShowLabel){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
)
|
||||
),
|
||||
dotData: FlDotData(
|
||||
show: true,
|
||||
getDotPainter: (p0, p1, p2, p3) {
|
||||
return FlDotCirclePainter(
|
||||
radius: 1,
|
||||
color: ColorPalette.white,
|
||||
strokeColor: ColorPalette.primary,
|
||||
strokeWidth: 2,
|
||||
);
|
||||
},
|
||||
checkToShowDot: (spot, barData) {
|
||||
if(spot.x == spotIndicator && !isShowLabel){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
titlesData: FlTitlesData(
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
reservedSize: 56,
|
||||
),
|
||||
drawBelowEverything: true,
|
||||
),
|
||||
rightTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
reservedSize: 36,
|
||||
interval: 1,
|
||||
),
|
||||
drawBelowEverything: true,
|
||||
),
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(showTitles: false),
|
||||
),
|
||||
),
|
||||
gridData: FlGridData(
|
||||
show: false
|
||||
),
|
||||
borderData: FlBorderData(show: false),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -2,11 +2,23 @@ import 'dart:math';
|
|||
|
||||
import 'package:cims_apps/application/assets/path_assets.dart';
|
||||
import 'package:cims_apps/application/component/button/button_back.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/text_form/text_form_view.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/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:flutter/material.dart';
|
||||
import 'package:group_button/group_button.dart';
|
||||
|
||||
class Time {
|
||||
int value;
|
||||
String simpleName, completeName;
|
||||
|
||||
Time(this.value, this.simpleName, this.completeName);
|
||||
}
|
||||
|
||||
class ProductView extends StatefulWidget {
|
||||
final String investType;
|
||||
|
@ -17,16 +29,64 @@ class ProductView extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ProductViewState extends State<ProductView> {
|
||||
int chooseTab = 0;
|
||||
String chooseTime = '1Y';
|
||||
int chooseMachine = 0;
|
||||
int selectedTab = 0;
|
||||
Time selectedTime = Time(2, '1D', '1 day');
|
||||
int selectedMachineType = 0;
|
||||
int selectedMachineTime = 0;
|
||||
|
||||
List<String> listTime = ['1D', '1M', '3M', 'YTD', '1Y', '3Y', '5Y', '10Y', 'All'];
|
||||
List<Time> listTime = [
|
||||
Time(2, '1D', '1 day'),
|
||||
Time(30, '1M', '1 month'),
|
||||
Time(90, '3M', '3 months'),
|
||||
Time(365, '1Y', '1 year'),
|
||||
];
|
||||
|
||||
List<String> listTab = ['NAV', 'AUM'];
|
||||
|
||||
List<String> listMachine = ['Monthly Routine', 'Connect Once'];
|
||||
|
||||
List<Time> listMachineTime = [
|
||||
Time(1, '1D', '1 year'),
|
||||
Time(3, '1M', '3 year'),
|
||||
Time(5, '3M', '5 year'),
|
||||
Time(10, '1Y', '10 year'),
|
||||
];
|
||||
|
||||
TextEditingController machineController = TextEditingController();
|
||||
|
||||
GroupButtonController machineGroupButtonController = GroupButtonController(selectedIndex: 0);
|
||||
|
||||
List<String> listTopHoldings = [
|
||||
'Bank Pembangunan Daerah Sulawesi Selatan dan Sulawesi Barat',
|
||||
'PT. Bank Jabar Banten, TBK',
|
||||
'PT. Bank Mega',
|
||||
'PT. Bank Nagaria',
|
||||
'PT. BPD Sulawesi Tengah'
|
||||
];
|
||||
|
||||
List<double> data = [];
|
||||
double estimatedValue = 0;
|
||||
|
||||
void setEstimatedValue() {
|
||||
double parseValue = double.parse(machineController.text.replaceAll('Rp ', '').replaceAll('.', ''));
|
||||
int machineType = selectedMachineType == 0 ? 12 : 1;
|
||||
setState(() {
|
||||
estimatedValue = (machineType * (listMachineTime[selectedMachineTime].value) * (parseValue * 10/100)) + parseValue;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
machineController.text = NumberFormatter.numberCurrency(100000, 'Rp ', 'id_ID', decimalDigits: 0);
|
||||
selectedTime = listTime[0];
|
||||
setEstimatedValue();
|
||||
List.generate(2, (index) => {
|
||||
data.add((2500 + index - Random().nextInt(100)).toDouble())
|
||||
});
|
||||
// TODO: implement initState
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -119,31 +179,31 @@ class _ProductViewState extends State<ProductView> {
|
|||
}
|
||||
|
||||
Widget contentContainer() {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
),
|
||||
child: ListView(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Wrap(
|
||||
spacing: 8,
|
||||
children: [
|
||||
Text(listTab[chooseTab]),
|
||||
const Text('26 Jan 24'),
|
||||
],
|
||||
),
|
||||
ProductChartView(
|
||||
tabType: listTab[selectedTab],
|
||||
lastTime: selectedTime.completeName,
|
||||
dataChart: data,
|
||||
),
|
||||
Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: switchTab(listTab, (value) {
|
||||
setState(() {
|
||||
chooseTab = value;
|
||||
selectedTab = value;
|
||||
data.clear();
|
||||
List.generate(selectedTime.value, (index) => {
|
||||
data.add((2500 + index - Random().nextInt(100)).toDouble())
|
||||
});
|
||||
}, chooseTab),
|
||||
});
|
||||
}, selectedTab),
|
||||
),
|
||||
),
|
||||
swithTime(),
|
||||
|
@ -170,25 +230,43 @@ class _ProductViewState extends State<ProductView> {
|
|||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
columnInformation('Total AUM', '3,88%'),
|
||||
columnInformation('Avg.Yield Dec 23', '0%'),
|
||||
columnInformation('Risk Level', '1,99%')
|
||||
columnInformation('Avg.Yield Dec 23', '6,44%'),
|
||||
columnInformation('Risk Level', 'Conservative')
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
cardInformation('Informasi Investasi', informationInvest()),
|
||||
cardInformation('Investment Information', informationInvest()),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
cardInformation('Informasi Investasi', investCost()),
|
||||
cardInformation('Investment Costs', investCost()),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
cardInformation('Time Machine', timeMachine())
|
||||
cardInformation('Time Machine', timeMachine()),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
topFiveHoldings(),
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
cardInformation('Document', documentProduct()),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: ButtonView(
|
||||
name: 'Buy',
|
||||
onPressed: () {
|
||||
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -228,7 +306,7 @@ class _ProductViewState extends State<ProductView> {
|
|||
onTap(e.key);
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
duration: Duration(milliseconds: 300),
|
||||
duration: const Duration(milliseconds: 300),
|
||||
height: 35,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
|
@ -256,6 +334,7 @@ class _ProductViewState extends State<ProductView> {
|
|||
Widget swithTime() {
|
||||
return Container(
|
||||
color: ColorPalette.slate50,
|
||||
alignment: Alignment.center,
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
|
@ -265,7 +344,11 @@ class _ProductViewState extends State<ProductView> {
|
|||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
chooseTime = e;
|
||||
selectedTime = e;
|
||||
data.clear();
|
||||
List.generate(e.value, (index) => {
|
||||
data.add((2500 + index - Random().nextInt(100)).toDouble())
|
||||
});
|
||||
});
|
||||
},
|
||||
child: AnimatedContainer(
|
||||
|
@ -275,10 +358,10 @@ class _ProductViewState extends State<ProductView> {
|
|||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
color: chooseTime == e ? ColorPalette.primary : ColorPalette.slate50
|
||||
color: selectedTime == e ? ColorPalette.primary : ColorPalette.slate50
|
||||
),
|
||||
child: Text(e, style: TextStyle(
|
||||
color: chooseTime == e ? ColorPalette.white : ColorPalette.slate400,
|
||||
child: Text(e.simpleName, style: TextStyle(
|
||||
color: selectedTime == e ? ColorPalette.white : ColorPalette.slate400,
|
||||
fontWeight: FontWeight.w700
|
||||
)),
|
||||
),
|
||||
|
@ -293,20 +376,7 @@ class _ProductViewState extends State<ProductView> {
|
|||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
style: const TextStyle(
|
||||
color: ColorPalette.slate400
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
const Icon(Icons.info_outline_rounded, size: 16, color: ColorPalette.slate400)
|
||||
],
|
||||
),
|
||||
rowInformation(title),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
@ -317,6 +387,24 @@ class _ProductViewState extends State<ProductView> {
|
|||
);
|
||||
}
|
||||
|
||||
Widget rowInformation(String title) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
style: const TextStyle(
|
||||
color: ColorPalette.slate400,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
const Icon(Icons.info_outline_rounded, size: 16, color: ColorPalette.slate400)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget cardInformation(String title, Widget child) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 24),
|
||||
|
@ -329,7 +417,7 @@ class _ProductViewState extends State<ProductView> {
|
|||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextTitle(title: title, fontSize: 16),
|
||||
SizedBox(
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
child
|
||||
|
@ -339,9 +427,9 @@ class _ProductViewState extends State<ProductView> {
|
|||
}
|
||||
|
||||
Widget informationInvest() {
|
||||
return const Column(
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
|
@ -357,27 +445,15 @@ class _ProductViewState extends State<ProductView> {
|
|||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Custodian Bank',
|
||||
style: TextStyle(
|
||||
color: ColorPalette.slate400,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
),
|
||||
Text('HSBC INDONESIA')
|
||||
rowInformation('Custodian Bank'),
|
||||
const Text('HSBC INDONESIA')
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Depository Bank',
|
||||
style: TextStyle(
|
||||
color: ColorPalette.slate400,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
),
|
||||
Text('BCA')
|
||||
rowInformation('Depository Bank'),
|
||||
const Text('BCA')
|
||||
],
|
||||
)
|
||||
],
|
||||
|
@ -385,9 +461,9 @@ class _ProductViewState extends State<ProductView> {
|
|||
}
|
||||
|
||||
Widget investCost(){
|
||||
return const Column(
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
const Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
|
@ -397,20 +473,22 @@ class _ProductViewState extends State<ProductView> {
|
|||
fontWeight: FontWeight.w600
|
||||
),
|
||||
),
|
||||
Text('Free')
|
||||
Text('Free',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Sales Commission',
|
||||
rowInformation('Sales Commission'),
|
||||
const Text('Free',
|
||||
style: TextStyle(
|
||||
color: ColorPalette.slate400,
|
||||
fontWeight: FontWeight.w600
|
||||
fontWeight: FontWeight.bold
|
||||
),
|
||||
),
|
||||
Text('Free')
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
|
@ -419,13 +497,221 @@ class _ProductViewState extends State<ProductView> {
|
|||
|
||||
Widget timeMachine() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
switchTab(listMachine, (value) {
|
||||
setState(() {
|
||||
chooseMachine = value;
|
||||
selectedMachineType = value;
|
||||
});
|
||||
setEstimatedValue();
|
||||
},
|
||||
chooseMachine
|
||||
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
|
||||
),
|
||||
),
|
||||
),
|
||||
TextFormView(
|
||||
name: '',
|
||||
ctrl: machineController,
|
||||
onChanged: (value) {
|
||||
value = value.replaceAll('Rp ', '').replaceAll('.', '');
|
||||
double parseValue = double.parse(value);
|
||||
if(value.isNotEmpty){
|
||||
machineController.text = NumberFormatter.numberCurrency(parseValue, 'Rp ', 'id_ID', decimalDigits: 0);
|
||||
}else{
|
||||
machineController.text = NumberFormatter.numberCurrency(0, 'Rp ', 'id_ID', decimalDigits: 0);
|
||||
}
|
||||
setEstimatedValue();
|
||||
},
|
||||
keyboardType: TextInputType.number,
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 16, bottom: 8),
|
||||
child: Text(
|
||||
"How many years ago did you start investing?",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: ColorPalette.slate800
|
||||
),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: GroupButton(
|
||||
buttons: listMachineTime.map((e) => e.completeName).toList(),
|
||||
controller: machineGroupButtonController,
|
||||
onSelected: (value, index, isSelected) {
|
||||
setState(() {
|
||||
selectedMachineTime = index;
|
||||
});
|
||||
setEstimatedValue();
|
||||
},
|
||||
options: GroupButtonOptions(
|
||||
buttonWidth: SizeConfig.width * .375,
|
||||
mainGroupAlignment: MainGroupAlignment.spaceBetween,
|
||||
groupRunAlignment: GroupRunAlignment.spaceBetween,
|
||||
spacing: 16,
|
||||
elevation: 0,
|
||||
borderRadius: BorderRadius.circular(80),
|
||||
selectedShadow: const [],
|
||||
selectedColor: Colors.white,
|
||||
selectedBorderColor: ColorPalette.primary,
|
||||
selectedTextStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: ColorPalette.primary
|
||||
),
|
||||
unselectedShadow: const [],
|
||||
unselectedBorderColor: ColorPalette.slate200,
|
||||
unselectedTextStyle: const TextStyle(
|
||||
fontWeight: FontWeight.w500,
|
||||
color: ColorPalette.slate500
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 16, bottom: 8),
|
||||
child: Text(
|
||||
"Today's Investment Estimated Value",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: ColorPalette.slate500
|
||||
),
|
||||
),
|
||||
),
|
||||
TextTitle(
|
||||
title: NumberFormatter.numberCurrency(estimatedValue, 'Rp ', 'id_ID'),
|
||||
fontSize: 24,
|
||||
color: ColorPalette.primary,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget topFiveHoldings() {
|
||||
return Column(
|
||||
children: [
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
TextTitle(title: 'Top 5 Holdings'),
|
||||
Text(
|
||||
'As of 31 Dec 2023',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: ColorPalette.slate400
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
...listTopHoldings.asMap().entries.map((e) {
|
||||
return topProduct(e.key, e.value);
|
||||
})
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget topProduct(int index, String name) {
|
||||
return Column(
|
||||
children: [
|
||||
if(index != 0)...[
|
||||
const Padding(
|
||||
padding: EdgeInsets.symmetric(vertical: 16),
|
||||
child: Divider(color: ColorPalette.slate200,),
|
||||
)
|
||||
],
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 11),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: ColorPalette.blue50,
|
||||
border: Border.all(color: ColorPalette.blue200)
|
||||
),
|
||||
child: Text(index.toString(), style: const TextStyle(color: ColorPalette.primary)),
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
name,
|
||||
style: const TextStyle(
|
||||
color: ColorPalette.slate800,
|
||||
fontSize: 16
|
||||
),
|
||||
)
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget documentProduct() {
|
||||
return Column(
|
||||
children: [
|
||||
Wrap(
|
||||
runAlignment: WrapAlignment.spaceBetween,
|
||||
alignment: WrapAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: SizeConfig.width * .4,
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.file_copy_outlined, color: ColorPalette.primary),
|
||||
SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Text(
|
||||
'Prospektus',
|
||||
style: TextStyle(
|
||||
color: ColorPalette.primary,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: SizeConfig.width * .4,
|
||||
child: const Row(
|
||||
children: [
|
||||
Icon(Icons.file_copy_outlined, color: ColorPalette.primary),
|
||||
SizedBox(
|
||||
width: 16,
|
||||
),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Fun Fact Sheet',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
color: ColorPalette.primary,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
],
|
||||
);
|
||||
|
|
|
@ -187,6 +187,14 @@ packages:
|
|||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
group_button:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: group_button
|
||||
sha256: "0610fcf28ed122bfb4b410fce161a390f7f2531d55d1d65c5375982001415940"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.3.4"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -43,6 +43,7 @@ dependencies:
|
|||
intl: ^0.19.0
|
||||
carousel_slider: ^4.2.1
|
||||
provider: ^6.1.1
|
||||
group_button: ^5.3.4
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user