diff --git a/assets/icons/icon-portofolio-bonds.png b/assets/icons/icon-portofolio-bonds.png new file mode 100644 index 0000000..58f5130 Binary files /dev/null and b/assets/icons/icon-portofolio-bonds.png differ diff --git a/assets/icons/icon-portofolio-moneymarket.png b/assets/icons/icon-portofolio-moneymarket.png new file mode 100644 index 0000000..de2f2f5 Binary files /dev/null and b/assets/icons/icon-portofolio-moneymarket.png differ diff --git a/assets/icons/icon-portofolio-shares.png b/assets/icons/icon-portofolio-shares.png new file mode 100644 index 0000000..7ec4b3c Binary files /dev/null and b/assets/icons/icon-portofolio-shares.png differ diff --git a/assets/icons/icon-portofolio-sharia.png b/assets/icons/icon-portofolio-sharia.png new file mode 100644 index 0000000..04c348a Binary files /dev/null and b/assets/icons/icon-portofolio-sharia.png differ diff --git a/assets/images/img-articles.png b/assets/images/img-articles.png new file mode 100644 index 0000000..ab41cc1 Binary files /dev/null and b/assets/images/img-articles.png differ diff --git a/assets/images/img-carousel.png b/assets/images/img-carousel.png new file mode 100644 index 0000000..2fb419e Binary files /dev/null and b/assets/images/img-carousel.png differ diff --git a/assets/images/img-dashboard-account.png b/assets/images/img-dashboard-account.png new file mode 100644 index 0000000..c6a152c Binary files /dev/null and b/assets/images/img-dashboard-account.png differ diff --git a/lib/application/assets/path_assets.dart b/lib/application/assets/path_assets.dart index 01a9b0b..edd97a7 100644 --- a/lib/application/assets/path_assets.dart +++ b/lib/application/assets/path_assets.dart @@ -10,6 +10,10 @@ class PathAssets { static const String iconGoogle = 'assets/icons/icon-google.png'; static const String icon1 = 'assets/icons/icon-1.png'; static const String iconConnect = 'assets/icons/icon-connect.png'; + static const String iconPortofolioBonds = 'assets/icons/icon-portofolio-bonds.png'; + static const String iconPortofolioShares = 'assets/icons/icon-portofolio-shares.png'; + static const String iconPortofolioSharia = 'assets/icons/icon-portofolio-sharia.png'; + static const String iconPortofolioMoneyMarket = 'assets/icons/icon-portofolio-moneymarket.png'; /// IMAGE static const String imgSplashLogo = 'assets/images/splash-logo.png'; @@ -22,4 +26,7 @@ class PathAssets { static const String imgKtpCropped = 'assets/images/img-ktp-cropped.png'; static const String imgKtpClear = 'assets/images/img-ktp-clear.png'; static const String imgKtpBlur = 'assets/images/img-ktp-blur.png'; + static const String imgDashboardAccount = 'assets/images/img-dashboard-account.png'; + static const String imgCarousel = 'assets/images/img-carousel.png'; + static const String imgArticles = 'assets/images/img-articles.png'; } diff --git a/lib/application/component/text_title/text_title.dart b/lib/application/component/text_title/text_title.dart new file mode 100644 index 0000000..2440efa --- /dev/null +++ b/lib/application/component/text_title/text_title.dart @@ -0,0 +1,24 @@ +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:flutter/cupertino.dart'; + +class TextTitle extends StatelessWidget { + final String title; + final Color color; + const TextTitle({ + Key? key, + required this.title, + required this.color, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Text( + title, + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: color, + ), + ); + } +} diff --git a/lib/application/theme/color_palette.dart b/lib/application/theme/color_palette.dart index 724bd85..72154aa 100644 --- a/lib/application/theme/color_palette.dart +++ b/lib/application/theme/color_palette.dart @@ -73,5 +73,17 @@ class ColorPalette { static const Color chathamsBlue = Color(0xFF285BB9); static const Color background = Color(0xFFDADADA); static const Color backgroundBlueLight = Color(0xFFEBF3FD); + static const Color slate200 = Color(0xFFE2E8F0); + static const Color slate400 = Color(0xFF94A3B8); + static const Color slate500 = Color(0xFF64748B); static const Color slate800 = Color(0xFF1E293B); + static const Color purple100 = Color(0xFFEDE9FE); + static const Color purple500 = Color(0xFF8B5CF6); + static const Color orange100 = Color(0xFFFFEDD5); + static const Color orange500 = Color(0xFFF97316); + static const Color cyan100 = Color(0xFFCFFAFE); + static const Color cyan500 = Color(0xFF06B6D4); + static const Color green100 = Color(0xFFDCFCE7); + static const Color green400 = Color(0xFF4ADE80); + static const Color green500 = Color(0xFF16A34A); } diff --git a/lib/features/bottom_navigation_view.dart b/lib/features/bottom_navigation_view.dart index 239473e..53cd5b3 100644 --- a/lib/features/bottom_navigation_view.dart +++ b/lib/features/bottom_navigation_view.dart @@ -1,4 +1,6 @@ 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/portfolio/portfolio_view.dart'; import 'package:flutter/material.dart'; class BottomNavigationView extends StatefulWidget { @@ -15,15 +17,12 @@ class _BottomNavigationViewState extends State { Widget build(BuildContext context) { ///TODO: masukan pagenya dilistWidget ini List listWidget = [ - Container( - color: Colors.amberAccent, - ), + HomeView(), Container( color: Colors.redAccent, ), Container(), - Container(), - Container(), + PortofolioView(), Container(), ]; @@ -60,6 +59,7 @@ class _BottomNavigationViewState extends State { }, currentIndex: _selectedIndex, items: listNavigation, + type: BottomNavigationBarType.fixed, showUnselectedLabels: true, selectedItemColor: ColorPalette.primary, unselectedItemColor: Colors.black, diff --git a/lib/features/dashboard/dashboard_account/view/homepage/homepage_view.dart b/lib/features/dashboard/dashboard_account/view/homepage/homepage_view.dart new file mode 100644 index 0000000..258b3ea --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/homepage/homepage_view.dart @@ -0,0 +1,529 @@ +import 'package:carousel_slider/carousel_slider.dart'; +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/component/text_title/text_title.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/route/route.dart'; +import 'package:cims_apps/core/utils/size_config.dart'; +import 'package:cims_apps/features/dashboard/dashboard_account/view/invest_type/invest_type_view.dart'; +import 'package:flutter/material.dart'; + +class InvestType { + String name; + String iconImage; + + InvestType(this.name, this.iconImage); +} + +class StepVerification { + int currentStep; + List nameStep; + + StepVerification(this.currentStep, this.nameStep); +} + +class Article { + String image; + String title; + String type; + + Article(this.type, this.title, this.image); +} + +class HomeView extends StatefulWidget { + const HomeView({super.key}); + + @override + State createState() => _HomeViewState(); +} + +class _HomeViewState extends State { + bool seePortofolioValue = false; + + List listPortofolioType = [ + InvestType('Money Market', PathAssets.iconPortofolioMoneyMarket), + InvestType('Shares', PathAssets.iconPortofolioShares), + InvestType('Bonds', PathAssets.iconPortofolioBonds), + InvestType('Sharia', PathAssets.iconPortofolioSharia) + ]; + + StepVerification listStepVerification = StepVerification(1, [ + 'Registration', 'Verification', 'Complete' + ]); + + List
listArticle = [ + Article('Education', 'Menggali Potensi Pasar: Analisis Sebelum Memulai Investasi', PathAssets.imgArticles), + Article('News', 'Tren Investasi 2024: Peluang dan Risiko yang Perlu Diketahui', PathAssets.imgArticles), + Article('Education', 'Investasi Berkelanjutan: Mengenal Portofolio Hijau untuk Masa Depan', PathAssets.imgArticles), + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: const Color(0xffF8FAFC), + 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.only(left: 24, right: 16), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + 'Home', + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w700 + ), + ), + ElevatedButton( + onPressed: () { + + }, + style: ElevatedButton.styleFrom( + padding: const EdgeInsets.all(0), + backgroundColor: Colors.white, + foregroundColor: const Color(0xff2563EB), + elevation: 0, + shape: const CircleBorder() + ), + child: const Icon(Icons.notifications_outlined) + ) + ], + ), + ), + const SizedBox( + height: 32, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: portofolioValue(), + ), + const SizedBox( + height: 24, + ), + cardInvestType(), + const SizedBox( + height: 32, + ), + Expanded( + child: ListView( + padding: const EdgeInsets.all(0), + children: [ + cardVerification(), + const SizedBox( + height: 24, + ), + infoAndPromo(), + const SizedBox( + height: 24, + ), + articles(), + ], + ), + ), + ], + ) + ], + ), + ), + ); + } + + Widget portofolioValue() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text('Portofolio Value', style: TextStyle(color: Colors.white)), + const SizedBox( + width: 12, + ), + GestureDetector( + onTap: () { + setState(() { + seePortofolioValue = !seePortofolioValue; + }); + }, + child: const Icon(Icons.visibility_outlined, color: Color(0xff93C5FD)) + ) + ], + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + AnimatedCrossFade( + duration: const Duration(milliseconds: 300), + alignment: Alignment.center, + crossFadeState: seePortofolioValue ? CrossFadeState.showSecond : CrossFadeState.showFirst, + firstChild: RichText( + text: const TextSpan( + text: 'Rp ', + style: TextStyle(fontSize: 32, color: Color(0xff93C5FD), fontFamily: 'Manrope',), + children: [ + TextSpan( + text: '22.500.000', + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.bold, + color: Colors.white, + fontFamily: 'Manrope', + ), + ) + ] + ) + ), + secondChild: Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Wrap( + spacing: 7, + children: [ + for (int i = 0; i < 6; i++) + Container( + width: 12, + height: 12, + decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle), + ), + ], + ), + ), + ), + ], + ) + ], + ); + } + + Widget cardInvestType() { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + boxShadow: [ + BoxShadow( + spreadRadius: 2, + blurRadius: 4, + color: const Color(0xff1E293B).withOpacity(0.04) + ) + ] + ), + child: Wrap( + spacing: 10, + children: listPortofolioType.asMap().entries.map((e) { + return GestureDetector( + onTap: () { + routePush(context, page: InvestTypeView(e.value.name)); + }, + child: Container( + color: Colors.white, + width: SizeConfig.width * .18, + child: Column( + children: [ + ImageView(image: e.value.iconImage, height: SizeConfig.width * .12, width: SizeConfig.width * .12,), + const SizedBox( + height: 8, + ), + Text( + e.value.name, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600 + ), + ) + ], + ), + ), + ); + }).toList(), + ), + ); + } + + Widget cardVerification() { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 24), + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(12), + ), + child: Column( + children: [ + stepVerification(), + const SizedBox( + height: 24, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Row( + children: [ + Icon(Icons.verified, size: 18,), + SizedBox( + width: 12, + ), + Text( + 'Verified by PT Gemilang', + style: TextStyle( + color: ColorPalette.slate500 + ), + ) + ], + ), + Container( + width: 16, + height: 16, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: const Color(0xffCBD5E1), width: 1.5), + shape: BoxShape.circle + ), + ) + ], + ), + const SizedBox( + height: 16, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Icon(Icons.verified, size: 18,), + SizedBox( + width: 12, + ), + Text( + 'Verified by KSEI', + style: TextStyle( + color: ColorPalette.slate500 + ), + ) + ], + ), + Container( + width: 16, + height: 16, + decoration: BoxDecoration( + color: Colors.white, + border: Border.all(color: const Color(0xffCBD5E1), width: 1.5), + shape: BoxShape.circle + ), + ) + ], + ), + const SizedBox( + height: 16, + ), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: const Color(0xffEFF6FF), + borderRadius: BorderRadius.circular(12) + ), + child: const Column( + children: [ + Text( + 'Your registration is currently being verified by PT Gemilang', + style: TextStyle( + color: ColorPalette.slate500 + ), + ), + SizedBox( + height: 16, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Estimated:', + style: TextStyle( + color: ColorPalette.slate500 + ), + ), + Text( + 'January 30 2024', + style: TextStyle( + fontWeight: FontWeight.w600, + color: Color(0xff1E293B) + ), + ) + ], + ) + ], + ), + ) + ], + ), + ); + } + + Widget stepVerification() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: listStepVerification.nameStep.asMap().entries.map((e) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if(e.key != 0) + SizedBox( + width: 30, + height: 30, + child: Divider(color: listStepVerification.currentStep >= e.key ? const Color(0xff2563EB) : const Color(0xffCBD5E1),), + ), + Column( + children: [ + Container( + width: 30, + height: 30, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: listStepVerification.currentStep <= e.key ? Colors.white : const Color(0xff2563EB), + border: Border.all( + color: listStepVerification.currentStep < e.key ? const Color(0xffCBD5E1) : const Color(0xff2563EB), + width: 2 + ) + ), + child: listStepVerification.currentStep <= e.key ? const SizedBox() : const Icon(Icons.done_rounded, color: Colors.white,), + ), + const SizedBox( + height: 8, + ), + Text(e.value, style: TextStyle(color: listStepVerification.currentStep == e.key ? const Color(0xff2563EB) : Colors.black),) + ], + ), + ], + ); + }).toList(), + ); + } + + Widget infoAndPromo() { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const TextTitle(title: 'Info and Special Promo', color: ColorPalette.slate800,), + const SizedBox( + height: 16, + ), + CarouselSlider( + items: [1,2,3].map((e) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: ImageView(image: PathAssets.imgCarousel, height: 150, width: SizeConfig.width * .9,), + ); + }).toList(), + options: CarouselOptions( + height: 150, + autoPlay: true, + pageSnapping: true, + viewportFraction: 0.9, + autoPlayInterval: const Duration(seconds: 5), + autoPlayCurve: Curves.fastOutSlowIn, + ), + ) + ], + ), + ); + } + + Widget articles() { + return Container( + color: Colors.white, + padding: const EdgeInsets.symmetric(vertical: 24), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const TextTitle(title: 'Article', color: ColorPalette.slate800), + GestureDetector( + onTap: () { + + }, + child: const Text('See More', + style: TextStyle( + color: ColorPalette.primary, + fontWeight: FontWeight.bold + ),), + ) + ], + ), + ), + const SizedBox( + height: 16, + ), + ...listArticle.asMap().entries.map((e) { + return Column( + children: [ + if(e.key != 0)...[ + const Padding( + padding: EdgeInsets.symmetric(vertical: 12), + child: Divider(color: ColorPalette.slate200,), + ) + ], + cardArticle(e.value), + ], + ); + }) + ], + ), + ); + } + + Widget cardArticle(Article article) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: Row( + children: [ + ImageView(image: PathAssets.imgArticles, width: SizeConfig.width * .17, height: SizeConfig.height * .08, borderRadius: 8,), + const SizedBox( + width: 16, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(article.title, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + const SizedBox( + height: 8, + ), + Container( + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(30), + color: ColorPalette.colorSwitchButtonActive + ), + child: Text(article.type), + ), + ], + ) + ) + ], + ), + ); + } +} 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 new file mode 100644 index 0000000..bdff514 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/invest_type/invest_type_view.dart @@ -0,0 +1,227 @@ +import 'package:cims_apps/application/assets/path_assets.dart'; +import 'package:cims_apps/application/component/image/image_view.dart'; +import 'package:cims_apps/application/component/text_title/text_title.dart'; +import 'package:cims_apps/application/theme/color_palette.dart'; +import 'package:cims_apps/core/utils/size_config.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); +} + +class InvestTypeView extends StatefulWidget { + final String title; + const InvestTypeView(this.title, {super.key}); + + @override + State createState() => _InvestTypeViewState(); +} + +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) + ]; + + @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, + children: [ + SizedBox( + width: SizeConfig.width * 0.1, + child: IconButton( + style: IconButton.styleFrom( + backgroundColor: Colors.white, + shape: const CircleBorder() + ), + onPressed: () { + + }, + icon: const Icon(Icons.arrow_back) + ), + ), + 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 cardProduct(e.value); + }).toList(), + ) + ], + ), + ) + ], + ) + ], + ), + ), + ); + } + + Widget filters() { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + segmentFilter(const Icon(Icons.filter_alt_outlined), 'Filter', () { }), + segmentFilter(const RotatedBox(quarterTurns: 1, child: Icon(Icons.compare_arrows)), 'Sort', () { }), + segmentFilter(const Icon(Icons.dashboard_outlined), 'Compare', () { }), + ], + ), + ); + } + + Widget segmentFilter(Widget leading, String text, void Function()? onTap) { + return GestureDetector( + onTap: onTap, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), + decoration: BoxDecoration( + border: Border.all(color: ColorPalette.slate200), + borderRadius: BorderRadius.circular(56) + ), + child: Row( + children: [ + leading, + const SizedBox( + width: 4, + ), + Text( + text, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: ColorPalette.slate500, + fontWeight: FontWeight.w700 + ), + ) + ], + ), + ), + ); + } + + Widget cardProduct(Product product) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + border: Border.all(color: ColorPalette.slate200), + ), + child: Column( + children: [ + Row( + children: [ + Text( + product.name, + style: TextStyle( + fontWeight: FontWeight.bold, + fontSize: 18 + ), + ) + ], + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 12), + child: Divider(color: ColorPalette.slate200), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + children: [ + Text('Yield', style: TextStyle(color: ColorPalette.slate400, fontWeight: FontWeight.w600),), + Row( + children: [ + Text( + '${product.yield.toString()} %', + style: TextStyle( + color: ColorPalette.green400, + fontWeight: FontWeight.w600 + ), + ), + Text(' / '), + Text('3year', style: TextStyle(color: ColorPalette.slate400, fontWeight: FontWeight.w600),) + ], + ) + ], + ), + Column( + children: [ + Text('Price/unit', style: TextStyle(color: ColorPalette.slate400, fontWeight: FontWeight.w600),), + Row( + children: [ + Text( + product.priceUnit.toString(), + style: TextStyle( + fontWeight: FontWeight.w600 + ), + ), + Icon(Icons.trending_up_outlined, size: 18, color: ColorPalette.green400,) + ], + ) + ], + ), + Column( + children: [ + Text('Managed funds', style: TextStyle(color: ColorPalette.slate400, fontWeight: FontWeight.w600),), + Row( + children: [ + Text( + product.funds.toString(), + style: TextStyle( + fontWeight: FontWeight.w600 + ), + ), + ], + ) + ], + ) + ], + ) + ], + ), + ); + } + +} diff --git a/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart b/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart new file mode 100644 index 0000000..5d1b325 --- /dev/null +++ b/lib/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart @@ -0,0 +1,347 @@ +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:cims_apps/core/utils/size_config.dart'; +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; + +class CategoryPortofolio { + String name; + int value; + + CategoryPortofolio(this.value, this.name); +} + +class PortofolioView extends StatefulWidget { + const PortofolioView({super.key}); + + @override + State createState() => _PortofolioViewState(); +} + +class _PortofolioViewState extends State { + List contentColor = [ + ColorPalette.purple500, + ColorPalette.orange500, + ColorPalette.cyan500, + ColorPalette.green500, + ]; + List bgContentColor = [ + ColorPalette.purple100, + ColorPalette.orange100, + ColorPalette.cyan100, + ColorPalette.green100, + ]; + + bool seePortofolioValue = false; + int touchedIndex = -1; + + List listCategoryPortofolio = [ + CategoryPortofolio(20, 'Money Market'), + CategoryPortofolio(15, 'Shares'), + CategoryPortofolio(8, 'Bonds'), + CategoryPortofolio(50, 'Sharia'), + ]; + + List assetsImage = [ + PathAssets.iconPortofolioMoneyMarket, + PathAssets.iconPortofolioShares, + PathAssets.iconPortofolioBonds, + PathAssets.iconPortofolioSharia, + ]; + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + body: SizedBox( + width: SizeConfig.width, + height: SizeConfig.height, + child: Stack( + children: [ + const ImageView(image: PathAssets.imgDashboardAccount), + Column( + children: [ + const SizedBox( + height: 50, + ), + const Center( + child: Text( + 'Portofolio', + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + color: Colors.white), + ), + ), + const SizedBox( + height: 40, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: portofolioValue(), + ), + Expanded( + child: Container( + margin: const EdgeInsets.only(top: 24), + decoration: const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(24), + topRight: Radius.circular(24)), + ), + child: ListView( + padding: const EdgeInsets.symmetric(vertical: 0), + children: [ + Stack(alignment: Alignment.center, children: [ + AspectRatio( + aspectRatio: 1.3, + child: PieChart(PieChartData( + pieTouchData: PieTouchData( + touchCallback: + (FlTouchEvent event, pieTouchResponse) { + setState(() { + if (!event.isInterestedForInteractions || + pieTouchResponse == null || + pieTouchResponse.touchedSection == + null) { + touchedIndex = -1; + return; + } + touchedIndex = pieTouchResponse + .touchedSection!.touchedSectionIndex; + }); + }, + ), + borderData: FlBorderData( + show: true, + ), + sectionsSpace: 20, + centerSpaceRadius: 100, + sections: sectionsDataPortofolio())), + ), + const Column( + children: [ + Text('Total Mutual Fund'), + Text('10', + style: TextStyle( + fontSize: 44, + fontWeight: FontWeight.w700)), + ], + ) + ]), + const SizedBox( + height: 12, + ), + menuPortofolio(), + const SizedBox( + height: 24, + ), + ...listColumnPortofolio(), + const SizedBox( + height: 24, + ) + ], + ), + ), + ) + ], + ) + ], + ), + ), + ); + } + + Widget portofolioValue() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Text('Portofolio Value', + style: TextStyle(color: Colors.white)), + const SizedBox( + width: 8, + ), + GestureDetector( + onTap: () { + setState(() { + seePortofolioValue = !seePortofolioValue; + }); + }, + child: const Icon(Icons.visibility_outlined, + color: Color(0xff93C5FD))) + ], + ), + const SizedBox( + height: 4, + ), + Row( + children: [ + AnimatedCrossFade( + duration: const Duration(milliseconds: 300), + alignment: Alignment.center, + crossFadeState: seePortofolioValue + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + firstChild: RichText( + text: const TextSpan( + text: 'Rp ', + style: TextStyle(fontSize: 32, color: Color(0xff93C5FD), fontFamily: 'Manrope'), + children: [ + TextSpan( + text: '22.500.000', + style: TextStyle( + fontSize: 32, + fontFamily: 'Manrope', + fontWeight: FontWeight.bold, + color: Colors.white + ), + ) + ])), + secondChild: Padding( + padding: const EdgeInsets.symmetric(vertical: 16), + child: Wrap( + spacing: 7, + children: [ + for (int i = 0; i < 6; i++) + Container( + width: 12, + height: 12, + decoration: const BoxDecoration( + color: Colors.white, shape: BoxShape.circle), + ), + ], + ), + ), + ), + ], + ) + ], + ); + } + + List sectionsDataPortofolio() { + return listCategoryPortofolio.asMap().entries.map((e) { + final isTouched = e.key == touchedIndex; + final radius = isTouched ? 40.0 : 30.0; + return PieChartSectionData( + color: contentColor[e.key], + value: e.value.value.toDouble(), + title: e.value.name, + showTitle: isTouched, + radius: radius, + titleStyle: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ); + }).toList(); + } + + Widget menuPortofolio() { + return Container( + margin: const EdgeInsets.symmetric(horizontal: 24), + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: const BorderRadius.all(Radius.circular(12)), + boxShadow: [ + BoxShadow( + spreadRadius: 2, + blurRadius: 8, + color: const Color(0xff1E293B).withOpacity(0.04)) + ]), + child: Wrap( + alignment: WrapAlignment.center, + spacing: 12, + children: listCategoryPortofolio.asMap().entries.map((e) { + return Container( + color: Colors.white, + width: SizeConfig.width * 0.18, + child: Column( + children: [ + Container( + height: 58, + width: 58, + alignment: Alignment.center, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: bgContentColor[e.key], + ), + child: Text( + '${e.value.value} %', + style: TextStyle( + fontWeight: FontWeight.bold, + color: contentColor[e.key]), + ), + ), + const SizedBox( + height: 8, + ), + Text( + e.value.name, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ) + ], + ), + ); + }).toList(), + ), + ); + } + + List listColumnPortofolio() { + return listCategoryPortofolio.asMap().entries.map((e) { + return Column( + children: [ + if (e.key > 0) ...[ + const Divider( + color: ColorPalette.slate200, + ) + ], + ListTile( + contentPadding: + const EdgeInsets.symmetric(vertical: 0, horizontal: 24), + leading: ImageView( + image: assetsImage[e.key], + width: SizeConfig.width * .15, + height: SizeConfig.height * .08, + borderRadius: 8, + ), + title: Text( + e.value.name, + style: const TextStyle(fontWeight: FontWeight.w600), + ), + subtitle: Text( + e.value.name, + style: const TextStyle( + fontWeight: FontWeight.w600, color: Color(0xff94A3B8)), + ), + trailing: const Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + 'Rp 2.000.000', + style: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.w700), + ), + Text( + '+ Rp 200.000', + style: TextStyle( + fontSize: 14, + color: Color(0xff4ADE80), + fontWeight: FontWeight.w600), + ) + ], + ), + ), + ], + ); + }).toList(); + } +} diff --git a/lib/features/dashboard/dashboard_public/view/dashboard_public_view.dart b/lib/features/dashboard/dashboard_public/view/dashboard_public_view.dart index d32f1be..4be6827 100644 --- a/lib/features/dashboard/dashboard_public/view/dashboard_public_view.dart +++ b/lib/features/dashboard/dashboard_public/view/dashboard_public_view.dart @@ -5,6 +5,7 @@ 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:cims_apps/features/auth/registration/view/registration_view.dart'; +import 'package:cims_apps/features/bottom_navigation_view.dart'; import 'package:flutter/material.dart'; class DashboardPublicView extends StatelessWidget { @@ -68,7 +69,9 @@ class DashboardPublicView extends StatelessWidget { isOutlined: true, width: SizeConfig.width * .43, height: SizeConfig.height * .06, - onPressed: () {}, + onPressed: () { + routePush(context, page: const BottomNavigationView()); + }, ), ButtonView( name: 'Sign Up', diff --git a/pubspec.lock b/pubspec.lock index c9172e4..d52e8a0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -49,6 +49,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + carousel_slider: + dependency: "direct main" + description: + name: carousel_slider + sha256: "9c695cc963bf1d04a47bd6021f68befce8970bcd61d24938e1fb0918cf5d9c42" + url: "https://pub.dev" + source: hosted + version: "4.2.1" characters: dependency: transitive description: @@ -97,6 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.6" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -129,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + fl_chart: + dependency: "direct main" + description: + name: fl_chart + sha256: "5a74434cc83bf64346efb562f1a06eefaf1bcb530dc3d96a104f631a1eff8d79" + url: "https://pub.dev" + source: hosted + version: "0.65.0" flutter: dependency: "direct main" description: flutter @@ -179,6 +203,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.0.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" js: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3ec0147..7ae0deb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,7 +39,9 @@ dependencies: flutter_svg: ^1.1.6 cached_network_image: ^3.2.3 remove_emoji_input_formatter: ^0.0.1+1 - + fl_chart: ^0.65.0 + intl: ^0.19.0 + carousel_slider: ^4.2.1 dev_dependencies: