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 chathamsBlue = Color(0xFF285BB9);
|
||||||
static const Color background = Color(0xFFDADADA);
|
static const Color background = Color(0xFFDADADA);
|
||||||
static const Color backgroundBlueLight = Color(0xFFEBF3FD);
|
static const Color backgroundBlueLight = Color(0xFFEBF3FD);
|
||||||
|
static const Color blue50 = Color(0xFFEFF6FF);
|
||||||
static const Color blue200 = Color(0xFFBFDBFE);
|
static const Color blue200 = Color(0xFFBFDBFE);
|
||||||
static const Color slate50 = Color(0xFFF8FAFC);
|
static const Color slate50 = Color(0xFFF8FAFC);
|
||||||
static const Color slate200 = Color(0xFFE2E8F0);
|
static const Color slate200 = Color(0xFFE2E8F0);
|
||||||
|
static const Color slate300 = Color(0xFFCBD5E1);
|
||||||
static const Color slate400 = Color(0xFF94A3B8);
|
static const Color slate400 = Color(0xFF94A3B8);
|
||||||
static const Color slate500 = Color(0xFF64748B);
|
static const Color slate500 = Color(0xFF64748B);
|
||||||
static const Color slate800 = Color(0xFF1E293B);
|
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(
|
Container(
|
||||||
padding: const EdgeInsets.all(12),
|
padding: const EdgeInsets.all(12),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color(0xffEFF6FF),
|
color: ColorPalette.blue50,
|
||||||
borderRadius: BorderRadius.circular(12)
|
borderRadius: BorderRadius.circular(12)
|
||||||
),
|
),
|
||||||
child: const Column(
|
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/assets/path_assets.dart';
|
||||||
import 'package:cims_apps/application/component/button/button_back.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/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/component/text_title/text_title.dart';
|
||||||
import 'package:cims_apps/application/theme/color_palette.dart';
|
import 'package:cims_apps/application/theme/color_palette.dart';
|
||||||
|
import 'package:cims_apps/core/utils/number_formatter.dart';
|
||||||
import 'package:cims_apps/core/utils/size_config.dart';
|
import 'package:cims_apps/core/utils/size_config.dart';
|
||||||
|
import 'package:cims_apps/features/dashboard/dashboard_account/view/product/product_chart_view.dart';
|
||||||
import 'package:flutter/material.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 {
|
class ProductView extends StatefulWidget {
|
||||||
final String investType;
|
final String investType;
|
||||||
|
@ -17,16 +29,64 @@ class ProductView extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ProductViewState extends State<ProductView> {
|
class _ProductViewState extends State<ProductView> {
|
||||||
int chooseTab = 0;
|
int selectedTab = 0;
|
||||||
String chooseTime = '1Y';
|
Time selectedTime = Time(2, '1D', '1 day');
|
||||||
int chooseMachine = 0;
|
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> listTab = ['NAV', 'AUM'];
|
||||||
|
|
||||||
List<String> listMachine = ['Monthly Routine', 'Connect Once'];
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -119,75 +179,93 @@ class _ProductViewState extends State<ProductView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget contentContainer() {
|
Widget contentContainer() {
|
||||||
return Container(
|
return ClipRRect(
|
||||||
decoration: BoxDecoration(
|
borderRadius: BorderRadius.circular(12),
|
||||||
borderRadius: BorderRadius.circular(12),
|
child: Container(
|
||||||
color: Colors.white,
|
decoration: BoxDecoration(
|
||||||
),
|
color: Colors.white,
|
||||||
child: ListView(
|
),
|
||||||
children: [
|
child: ListView(
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
ProductChartView(
|
||||||
child: Wrap(
|
tabType: listTab[selectedTab],
|
||||||
spacing: 8,
|
lastTime: selectedTime.completeName,
|
||||||
children: [
|
dataChart: data,
|
||||||
Text(listTab[chooseTab]),
|
|
||||||
const Text('26 Jan 24'),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
Center(
|
||||||
Center(
|
child: Padding(
|
||||||
child: Padding(
|
padding: const EdgeInsets.all(24),
|
||||||
padding: const EdgeInsets.all(24),
|
child: switchTab(listTab, (value) {
|
||||||
child: switchTab(listTab, (value) {
|
setState(() {
|
||||||
setState(() {
|
selectedTab = value;
|
||||||
chooseTab = value;
|
data.clear();
|
||||||
});
|
List.generate(selectedTime.value, (index) => {
|
||||||
}, chooseTab),
|
data.add((2500 + index - Random().nextInt(100)).toDouble())
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, selectedTab),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
swithTime(),
|
||||||
swithTime(),
|
const SizedBox(
|
||||||
const SizedBox(
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
columnInformation('CAGR 1Y', '3,88%'),
|
|
||||||
columnInformation('Drawdown 1Y', '0%'),
|
|
||||||
columnInformation('Expense Ratio', '1,99%')
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 16,
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
columnInformation('Total AUM', '3,88%'),
|
|
||||||
columnInformation('Avg.Yield Dec 23', '0%'),
|
|
||||||
columnInformation('Risk Level', '1,99%')
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(
|
|
||||||
height: 24,
|
height: 24,
|
||||||
),
|
),
|
||||||
cardInformation('Informasi Investasi', informationInvest()),
|
Padding(
|
||||||
const SizedBox(
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
height: 24,
|
child: Row(
|
||||||
),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
cardInformation('Informasi Investasi', investCost()),
|
children: [
|
||||||
const SizedBox(
|
columnInformation('CAGR 1Y', '3,88%'),
|
||||||
height: 24,
|
columnInformation('Drawdown 1Y', '0%'),
|
||||||
),
|
columnInformation('Expense Ratio', '1,99%')
|
||||||
cardInformation('Time Machine', timeMachine())
|
],
|
||||||
],
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
columnInformation('Total AUM', '3,88%'),
|
||||||
|
columnInformation('Avg.Yield Dec 23', '6,44%'),
|
||||||
|
columnInformation('Risk Level', 'Conservative')
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
cardInformation('Investment Information', informationInvest()),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
cardInformation('Investment Costs', investCost()),
|
||||||
|
const SizedBox(
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
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);
|
onTap(e.key);
|
||||||
},
|
},
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
duration: Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
height: 35,
|
height: 35,
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
@ -256,6 +334,7 @@ class _ProductViewState extends State<ProductView> {
|
||||||
Widget swithTime() {
|
Widget swithTime() {
|
||||||
return Container(
|
return Container(
|
||||||
color: ColorPalette.slate50,
|
color: ColorPalette.slate50,
|
||||||
|
alignment: Alignment.center,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
@ -265,7 +344,11 @@ class _ProductViewState extends State<ProductView> {
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
chooseTime = e;
|
selectedTime = e;
|
||||||
|
data.clear();
|
||||||
|
List.generate(e.value, (index) => {
|
||||||
|
data.add((2500 + index - Random().nextInt(100)).toDouble())
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
child: AnimatedContainer(
|
child: AnimatedContainer(
|
||||||
|
@ -275,10 +358,10 @@ class _ProductViewState extends State<ProductView> {
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(4),
|
borderRadius: BorderRadius.circular(4),
|
||||||
color: chooseTime == e ? ColorPalette.primary : ColorPalette.slate50
|
color: selectedTime == e ? ColorPalette.primary : ColorPalette.slate50
|
||||||
),
|
),
|
||||||
child: Text(e, style: TextStyle(
|
child: Text(e.simpleName, style: TextStyle(
|
||||||
color: chooseTime == e ? ColorPalette.white : ColorPalette.slate400,
|
color: selectedTime == e ? ColorPalette.white : ColorPalette.slate400,
|
||||||
fontWeight: FontWeight.w700
|
fontWeight: FontWeight.w700
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
|
@ -293,20 +376,7 @@ class _ProductViewState extends State<ProductView> {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
rowInformation(title),
|
||||||
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)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
|
@ -316,6 +386,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) {
|
Widget cardInformation(String title, Widget child) {
|
||||||
return Container(
|
return Container(
|
||||||
|
@ -329,7 +417,7 @@ class _ProductViewState extends State<ProductView> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
TextTitle(title: title, fontSize: 16),
|
TextTitle(title: title, fontSize: 16),
|
||||||
SizedBox(
|
const SizedBox(
|
||||||
height: 16,
|
height: 16,
|
||||||
),
|
),
|
||||||
child
|
child
|
||||||
|
@ -339,9 +427,9 @@ class _ProductViewState extends State<ProductView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget informationInvest() {
|
Widget informationInvest() {
|
||||||
return const Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
const Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
|
@ -357,27 +445,15 @@ class _ProductViewState extends State<ProductView> {
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
rowInformation('Custodian Bank'),
|
||||||
'Custodian Bank',
|
const Text('HSBC INDONESIA')
|
||||||
style: TextStyle(
|
|
||||||
color: ColorPalette.slate400,
|
|
||||||
fontWeight: FontWeight.w600
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text('HSBC INDONESIA')
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
rowInformation('Depository Bank'),
|
||||||
'Depository Bank',
|
const Text('BCA')
|
||||||
style: TextStyle(
|
|
||||||
color: ColorPalette.slate400,
|
|
||||||
fontWeight: FontWeight.w600
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text('BCA')
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -385,9 +461,9 @@ class _ProductViewState extends State<ProductView> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget investCost(){
|
Widget investCost(){
|
||||||
return const Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
const Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
|
@ -397,20 +473,22 @@ class _ProductViewState extends State<ProductView> {
|
||||||
fontWeight: FontWeight.w600
|
fontWeight: FontWeight.w600
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text('Free')
|
Text('Free',
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.bold
|
||||||
|
),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
rowInformation('Sales Commission'),
|
||||||
'Sales Commission',
|
const Text('Free',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: ColorPalette.slate400,
|
fontWeight: FontWeight.bold
|
||||||
fontWeight: FontWeight.w600
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
Text('Free')
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -419,13 +497,221 @@ class _ProductViewState extends State<ProductView> {
|
||||||
|
|
||||||
Widget timeMachine() {
|
Widget timeMachine() {
|
||||||
return Column(
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
switchTab(listMachine, (value) {
|
switchTab(listMachine, (value) {
|
||||||
setState(() {
|
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
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
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:
|
http:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -43,6 +43,7 @@ dependencies:
|
||||||
intl: ^0.19.0
|
intl: ^0.19.0
|
||||||
carousel_slider: ^4.2.1
|
carousel_slider: ^4.2.1
|
||||||
provider: ^6.1.1
|
provider: ^6.1.1
|
||||||
|
group_button: ^5.3.4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user