Compare commits
	
		
			47 Commits
		
	
	
		
			83211e76f9
			...
			yoga
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e441c56bab | |||
| e513df325c | |||
| 23e1a6628f | |||
| 4b07219928 | |||
| 9cdda42b8b | |||
| 6f5d3ccca8 | |||
| eb99ad9d7f | |||
| ff515e2621 | |||
| f057a346c2 | |||
| f84fe1017d | |||
| 27ba55314b | |||
| 59e046bd92 | |||
| 4461b78565 | |||
| f2f688f9f3 | |||
| d1adfd2ab0 | |||
| 33b2ab85e3 | |||
| 6a43a3dcaf | |||
| db1280b272 | |||
| ae4f9c25c4 | |||
| a87afe16cb | |||
| 1904e9a4a3 | |||
| d79959c47f | |||
| 38837bd4f8 | |||
| d966108e9e | |||
| 506f364b87 | |||
| 506480d812 | |||
| a6ea9a2314 | |||
| 176261923d | |||
| a3148d8210 | |||
| 6e03fa5fa7 | |||
| de1782c2c2 | |||
| 89a79276a6 | |||
| 4737a91ab1 | |||
| ff135dd47b | |||
| 2e98c1a234 | |||
| c4c0479341 | |||
| e510aaefd7 | |||
| dfb947dce5 | |||
| 9761dc369c | |||
| 9e2304990c | |||
| 8b1b3e950f | |||
| aa987fe320 | |||
| ad6195061d | |||
| b5a382ce96 | |||
| fdca27233b | |||
| bd065242e6 | |||
| a35110af71 | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-card.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-cat.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 7.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-education.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-faqs.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-fund.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-gadget-outline.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-home.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-logout.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-navigation-home.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-navigation-plan.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-navigation-portfolio.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-navigation-profile.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-navigation-transaction.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 899 B | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-padlock.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-profile.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-remove.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-setting.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.2 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-shop.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/img-dashboard-profile.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.0 MiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/img-empty-transaction.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 70 KiB | 
| @@ -49,6 +49,30 @@ class PathAssets { | ||||
|   static const String iconTicket = 'assets/icons/icon-ticket.png'; | ||||
|   static const String iconGadget = 'assets/icons/icon-gadget.png'; | ||||
|   static const String iconCar = 'assets/icons/icon-car.png'; | ||||
|   static const String iconNavigationHome = | ||||
|       'assets/icons/icon-navigation-home.png'; | ||||
|   static const String iconNavigationPlan = | ||||
|       'assets/icons/icon-navigation-plan.png'; | ||||
|   static const String iconNavigationTransaction = | ||||
|       'assets/icons/icon-navigation-transaction.png'; | ||||
|   static const String iconNavigationPortfolio = | ||||
|       'assets/icons/icon-navigation-portfolio.png'; | ||||
|   static const String iconNavigationProfile = | ||||
|       'assets/icons/icon-navigation-profile.png'; | ||||
|   static const String iconRemove = 'assets/icons/icon-remove.png'; | ||||
|   static const String iconCat = 'assets/icons/icon-cat.png'; | ||||
|   static const String iconProfile = 'assets/icons/icon-profile.png'; | ||||
|   static const String iconPadlock = 'assets/icons/icon-padlock.png'; | ||||
|   static const String iconCard = 'assets/icons/icon-card.png'; | ||||
|   static const String iconSetting = 'assets/icons/icon-setting.png'; | ||||
|   static const String iconFaqs = 'assets/icons/icon-faqs.png'; | ||||
|   static const String iconLogout = 'assets/icons/icon-logout.png'; | ||||
|   static const String iconEducation = 'assets/icons/icon-education.png'; | ||||
|   static const String iconFund = 'assets/icons/icon-fund.png'; | ||||
|   static const String iconHome = 'assets/icons/icon-home.png'; | ||||
|   static const String iconShop = 'assets/icons/icon-shop.png'; | ||||
|   static const String iconGadgetOutline = | ||||
|       'assets/icons/icon-gadget-outline.png'; | ||||
|  | ||||
|   /// IMAGE | ||||
|   static const String imgSplashLogo = 'assets/images/splash-logo.png'; | ||||
| @@ -83,7 +107,28 @@ class PathAssets { | ||||
|   static const String imgGuide1 = 'assets/images/img-guide1.png'; | ||||
|   static const String imgGuide2 = 'assets/images/img-guide2.png'; | ||||
|   static const String imgOpenShopping = 'assets/images/img-open-shopping.png'; | ||||
|   static const String imgPaymentSuccess = 'assets/images/img-payment-success.png'; | ||||
|   static const String imgPaymentSuccess = | ||||
|       'assets/images/img-payment-success.png'; | ||||
|   static const String frameSignature = 'assets/images/frame-signature.png'; | ||||
|   static const String imgFinish = 'assets/images/img-finish.png'; | ||||
|   static const String imgDashboardProfile = | ||||
|       'assets/images/img-dashboard-profile.png'; | ||||
|   static const String imgEmptyTransaction = | ||||
|       'assets/images/img-empty-transaction.png'; | ||||
|  | ||||
|   static const Map<String, String> goalInvestIcon = { | ||||
|     'Education': iconToga, | ||||
|     'Marriage': iconCake, | ||||
|     'Old age days': iconHouse, | ||||
|     'Home': iconHouse, | ||||
|     'Other Plan': iconCreatePlan, | ||||
|     'Create Plan': iconCreatePlan, | ||||
|     'Entertainment': iconTicket, | ||||
|     'Gadget': iconGadget, | ||||
|     'Business': iconMarket, | ||||
|     'Fashion': iconBag, | ||||
|     'Shop': iconBag, | ||||
|     'Vehicle': iconCar, | ||||
|     'Holiday': iconPlane, | ||||
|   }; | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,100 @@ | ||||
| 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:flutter/material.dart'; | ||||
|  | ||||
| class CardTransactionView extends StatelessWidget { | ||||
|   final VoidCallback onTap; | ||||
|   final String iconPath, type, amount, subs, step; | ||||
|   final String? timeTransaction; | ||||
|   const CardTransactionView({ | ||||
|     Key? key, | ||||
|     required this.step, | ||||
|     required this.type, | ||||
|     required this.amount, | ||||
|     required this.iconPath, | ||||
|     required this.subs, | ||||
|     required this.onTap, | ||||
|     this.timeTransaction, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     TextTheme textTheme = Theme.of(context).textTheme; | ||||
|     return GestureDetector( | ||||
|       onTap: onTap, | ||||
|       child: Container( | ||||
|         margin: const EdgeInsets.symmetric(vertical: 16.0), | ||||
|         padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 16.0), | ||||
|         decoration: BoxDecoration( | ||||
|           color: Colors.white, | ||||
|           border: Border.all(width: 1, color: ColorPalette.slate200), | ||||
|           borderRadius: const BorderRadius.all(Radius.circular(12)), | ||||
|         ), | ||||
|         child: Row( | ||||
|           mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|           crossAxisAlignment: CrossAxisAlignment.center, | ||||
|           children: [ | ||||
|             SizedBox( | ||||
|                 width: SizeConfig.width * .4, | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     Row( | ||||
|                       children: [ | ||||
|                         ImageView( | ||||
|                             image: iconPath, width: SizeConfig.width * .12), | ||||
|                         Padding( | ||||
|                           padding: const EdgeInsets.only(left: 8.0), | ||||
|                           child: Text( | ||||
|                             type, | ||||
|                             style: textTheme.headlineSmall, | ||||
|                           ), | ||||
|                         ), | ||||
|                       ], | ||||
|                     ), | ||||
|                     Padding( | ||||
|                       padding: const EdgeInsets.only(top: 16.0), | ||||
|                       child: Text( | ||||
|                         amount, | ||||
|                         style: textTheme.headlineSmall, | ||||
|                       ), | ||||
|                     ), | ||||
|                   ], | ||||
|                 )), | ||||
|             SizedBox( | ||||
|                 width: SizeConfig.width * .4, | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.end, | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     SizedBox( | ||||
|                         height: SizeConfig.height * .08, | ||||
|                         child: Text( | ||||
|                           subs, | ||||
|                           style: const TextStyle(color: ColorPalette.primary), | ||||
|                         )), | ||||
|                     Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.end, | ||||
|                       children: [ | ||||
|                         step == 'waiting' | ||||
|                             ? const Icon(Icons.access_time_sharp, | ||||
|                                 color: ColorPalette.slate400) | ||||
|                             : const SizedBox(), | ||||
|                         step == 'waiting' | ||||
|                             ? Text(timeTransaction.toString()) | ||||
|                             : const SizedBox(), | ||||
|                         const Padding( | ||||
|                           padding: EdgeInsets.only(left: 16.0), | ||||
|                           child: Icon(Icons.arrow_forward_ios), | ||||
|                         ), | ||||
|                       ], | ||||
|                     ), | ||||
|                   ], | ||||
|                 )), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,44 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class EmptyCardTransaction extends StatelessWidget { | ||||
|   final VoidCallback onPressedButton; | ||||
|   const EmptyCardTransaction({Key? key, required this.onPressedButton}) | ||||
|       : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     TextTheme textTheme = Theme.of(context).textTheme; | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.symmetric(vertical: 8.0), | ||||
|       child: Center( | ||||
|         child: Column( | ||||
|           mainAxisAlignment: MainAxisAlignment.spaceAround, | ||||
|           children: [ | ||||
|             ImageView( | ||||
|               image: PathAssets.imgEmptyTransaction, | ||||
|               width: SizeConfig.width * .4, | ||||
|             ), | ||||
|             Text( | ||||
|               'No Transaction Yet', | ||||
|               style: textTheme.headlineSmall, | ||||
|             ), | ||||
|             Text( | ||||
|               "Let's keep building your investment for even greater financial growth!", | ||||
|               style: textTheme.bodyMedium, | ||||
|               textAlign: TextAlign.center, | ||||
|             ), | ||||
|             ButtonView( | ||||
|               name: 'Investing Now', | ||||
|               width: SizeConfig.width * .5, | ||||
|               onPressed: onPressedButton, | ||||
|             ), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -11,9 +11,9 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { | ||||
|   const CustomAppBar({ | ||||
|     Key? key, | ||||
|     required this.height, | ||||
|     this.leading, | ||||
|     required this.title, | ||||
|     this.trailing, | ||||
|     this.leading, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   | ||||
							
								
								
									
										144
									
								
								lib/application/component/date_picker/date_picker_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,144 @@ | ||||
| import 'package:calendar_date_picker2/calendar_date_picker2.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/text_form/text_form_view.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:intl/intl.dart'; | ||||
|  | ||||
| class DatePickerView extends StatelessWidget { | ||||
|   final String name; | ||||
|   final TextEditingController ctrl; | ||||
|   final DateTime? minDate, maxDate; | ||||
|   final String? hintText, buttonName; | ||||
|   final bool isMultipleSelection, enabled; | ||||
|   final List<DateTime>? initialValue; | ||||
|   final ValueChanged<OnChangedDatePickerModel>? onChanged; | ||||
|   final ValueChanged<List<DateTime?>>? onFinish; | ||||
|   final FormFieldValidator<String>? validatorDate; | ||||
|   const DatePickerView( | ||||
|       {Key? key, | ||||
|       required this.name, | ||||
|       required this.ctrl, | ||||
|       this.minDate, | ||||
|       this.maxDate, | ||||
|       this.hintText, | ||||
|       this.buttonName, | ||||
|       required this.isMultipleSelection, | ||||
|       required this.enabled, | ||||
|       this.initialValue, | ||||
|       this.onChanged, | ||||
|       this.onFinish, | ||||
|       this.validatorDate}) | ||||
|       : super(key: key); | ||||
|  | ||||
|   String _dateFormat(DateTime? dateTime) => | ||||
|       dateTime != null ? DateFormat('dd/MM/yyyy').format(dateTime) : ""; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     List<DateTime?> initData = initialValue ?? []; | ||||
|     List<DateTime?> dateList = []; | ||||
|  | ||||
|     String dateLabel() { | ||||
|       return dateList.map((e) => _dateFormat(e)).join(" - "); | ||||
|     } | ||||
|  | ||||
|     onChangedDatePicker(List<DateTime?> value) { | ||||
|       if (isMultipleSelection) { | ||||
|         final pickerDateRange = PickerDateRange( | ||||
|           startDate: value[0] ?? DateTime.now(), | ||||
|           endDate: | ||||
|               value.length > 1 ? value[1] ?? DateTime.now() : DateTime.now(), | ||||
|         ); | ||||
|         onChanged | ||||
|             ?.call(OnChangedDatePickerModel(pickerDateRange: pickerDateRange)); | ||||
|       } else { | ||||
|         onChanged?.call(OnChangedDatePickerModel(dateTime: value[0])); | ||||
|       } | ||||
|       dateList = value; | ||||
|     } | ||||
|  | ||||
|     dialogDatePicker() { | ||||
|       showModalBottomSheet( | ||||
|         context: context, | ||||
|         isScrollControlled: true, | ||||
|         builder: (BuildContext context) { | ||||
|           return Container( | ||||
|             padding: const EdgeInsets.only(top: 16.0), | ||||
|             height: SizeConfig.height * .65, | ||||
|             child: Column( | ||||
|               // mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               mainAxisSize: MainAxisSize.min, | ||||
|               children: [ | ||||
|                 CalendarDatePicker2( | ||||
|                   value: [...initData], | ||||
|                   config: CalendarDatePicker2Config( | ||||
|                     centerAlignModePicker: true, | ||||
|                     calendarType: isMultipleSelection | ||||
|                         ? CalendarDatePicker2Type.range | ||||
|                         : CalendarDatePicker2Type.single, | ||||
|                     customModePickerIcon: const SizedBox(), | ||||
|                     firstDate: minDate ?? DateTime(1900), | ||||
|                     lastDate: maxDate, | ||||
|                   ), | ||||
|                   // initialValue: [...initData], | ||||
|                   onValueChanged: (value) { | ||||
|                     onChangedDatePicker(value); | ||||
|                   }, | ||||
|                 ), | ||||
|                 Padding( | ||||
|                   padding: const EdgeInsets.symmetric(horizontal: 16), | ||||
|                   child: ButtonView( | ||||
|                     width: SizeConfig.width, | ||||
|                     onPressed: () { | ||||
|                       if (dateList.isNotEmpty) { | ||||
|                         onFinish?.call(dateList); | ||||
|                         ctrl.text = dateLabel(); | ||||
|                         initData = dateList; | ||||
|                       } | ||||
|                       Navigator.pop(context); | ||||
|                     }, | ||||
|                     name: 'OK', | ||||
|                   ), | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ); | ||||
|         }, | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return TextFormView( | ||||
|       name: name, | ||||
|       hintText: hintText, | ||||
|       readOnly: true, | ||||
|       ctrl: ctrl, | ||||
|       validator: validatorDate, | ||||
|       enabled: enabled, | ||||
|       onTap: () { | ||||
|         if (enabled) dialogDatePicker(); | ||||
|       }, | ||||
|       suffixIcon: const UnconstrainedBox( | ||||
|         child: Icon( | ||||
|           Icons.calendar_today_rounded, | ||||
|           color: ColorPalette.slate400, | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
| class OnChangedDatePickerModel { | ||||
|   final DateTime? dateTime; | ||||
|   final PickerDateRange? pickerDateRange; | ||||
|  | ||||
|   OnChangedDatePickerModel({this.dateTime, this.pickerDateRange}); | ||||
| } | ||||
|  | ||||
| class PickerDateRange { | ||||
|   final DateTime startDate; | ||||
|   final DateTime endDate; | ||||
|  | ||||
|   PickerDateRange({required this.startDate, required this.endDate}); | ||||
| } | ||||
| @@ -0,0 +1,119 @@ | ||||
| import 'package:cims_apps/application/component/expandable_widget/see_more_less_widget.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 ExpandableWidget extends StatelessWidget { | ||||
|   final String? content; | ||||
|   final double? fontSize; | ||||
|   final int maxLinesToShow; | ||||
|   final Alignment? alignmentMore; | ||||
|   final bool? hideTextMore; | ||||
|   final bool? hideIconMore; | ||||
|  | ||||
|   ExpandableWidget({ | ||||
|     super.key, | ||||
|     required this.content, | ||||
|     this.fontSize, | ||||
|     this.maxLinesToShow = 1, | ||||
|     this.alignmentMore, | ||||
|     this.hideTextMore = false, | ||||
|     this.hideIconMore = true, | ||||
|   }); | ||||
|  | ||||
|   ValueNotifier<bool> expanded = ValueNotifier(false); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     final TextSpan textSpan = TextSpan( | ||||
|       text: content ?? "", | ||||
|       style: TextStyle( | ||||
|         fontSize: fontSize ?? 16.0, | ||||
|         color: ColorPalette.slate400, | ||||
|       ), | ||||
|     ); | ||||
|  | ||||
|     final TextPainter textPainter = TextPainter( | ||||
|       text: textSpan, | ||||
|       maxLines: expanded.value ? null : maxLinesToShow, | ||||
|       textDirection: TextDirection.ltr, | ||||
|       strutStyle: StrutStyle( | ||||
|         fontSize: fontSize ?? 16.0, | ||||
|       ) | ||||
|     ); | ||||
|  | ||||
|     textPainter.layout(maxWidth: SizeConfig.width); | ||||
|  | ||||
|     final int numberOfLines = textPainter.computeLineMetrics().length; | ||||
|     return ValueListenableBuilder( | ||||
|       valueListenable: expanded, | ||||
|       builder: (context, values, _) { | ||||
|         return Column( | ||||
|           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|           children: [ | ||||
|             LayoutBuilder( | ||||
|               builder: (BuildContext context, BoxConstraints constraints) { | ||||
|                 if (!expanded.value && numberOfLines >= maxLinesToShow) { | ||||
|                   return Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       Text( | ||||
|                         content ?? "", | ||||
|                         maxLines: maxLinesToShow, | ||||
|                         overflow: TextOverflow.ellipsis, | ||||
|                         style: TextStyle( | ||||
|                           fontSize: fontSize ?? 16.0, | ||||
|                           color: ColorPalette.slate400, | ||||
|                         ), | ||||
|                       ), | ||||
|                       /* See More :: type 1 - See More | 2 - See Less */ | ||||
|                       SeeMoreLessWidget( | ||||
|                         textData: 'See More', | ||||
|                         type: 1, | ||||
|                         section: 1, | ||||
|                         onSeeMoreLessTap: () { | ||||
|                           expanded.value = true; | ||||
|                         }, | ||||
|                         alignment: alignmentMore, | ||||
|                         hideIconMore: hideIconMore!, | ||||
|                         hideTextMore: hideTextMore!, | ||||
|                       ), | ||||
|                       /* See More :: type 1 - See More | 2 - See Less */ | ||||
|                     ], | ||||
|                   ); | ||||
|                 } else { | ||||
|                   return Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       Text( | ||||
|                         content ?? "", | ||||
|                         style: TextStyle( | ||||
|                           fontSize: fontSize ?? 16.0, | ||||
|                           color: ColorPalette.slate400, | ||||
|                         ), | ||||
|                       ), | ||||
|                       if (expanded.value && numberOfLines >= maxLinesToShow) | ||||
|                       /* See Less :: type 1 - See More | 2 - See Less */ | ||||
|                         SeeMoreLessWidget( | ||||
|                           textData: 'See Less', | ||||
|                           type: 2, | ||||
|                           section: 1, | ||||
|                           onSeeMoreLessTap: () { | ||||
|                             expanded.value = false; | ||||
|                           }, | ||||
|                           alignment: alignmentMore, | ||||
|                           hideIconMore: hideIconMore!, | ||||
|                           hideTextMore: hideTextMore!, | ||||
|                         ), | ||||
|                       /* See Less :: type 1 - See More | 2 - See Less */ | ||||
|                     ], | ||||
|                   ); | ||||
|                 } | ||||
|               }, | ||||
|             ), | ||||
|           ], | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,74 @@ | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class SeeMoreLessWidget extends StatelessWidget { | ||||
|   final String? textData; | ||||
|   final int? type; /* type 1 - See More | 2 - See Less */ | ||||
|   final Function? onSeeMoreLessTap; | ||||
|   final int? | ||||
|   section; /* 1: About the course | 2 - Who can take up this course? | 3 - Mentors | 4 - Course Video Reviews */ | ||||
|   final Alignment? alignment; | ||||
|   final bool hideTextMore; | ||||
|   final bool hideIconMore; | ||||
|  | ||||
|   const SeeMoreLessWidget({ | ||||
|     super.key, | ||||
|     required this.textData, | ||||
|     required this.type, | ||||
|     required this.onSeeMoreLessTap, | ||||
|     required this.section, | ||||
|     required this.alignment, | ||||
|     required this.hideTextMore, | ||||
|     required this.hideIconMore, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Align( | ||||
|       alignment: alignment ?? Alignment.centerLeft, | ||||
|       child: InkWell( | ||||
|         onTap: () { | ||||
|           if (onSeeMoreLessTap != null) { | ||||
|             onSeeMoreLessTap!(); | ||||
|           } | ||||
|         }, | ||||
|         child: Text.rich( | ||||
|           softWrap: true, | ||||
|           style: const TextStyle( | ||||
|             color: ColorPalette.primary, | ||||
|           ), | ||||
|           textAlign: TextAlign.start, | ||||
|           TextSpan( | ||||
|             text: "", | ||||
|             children: [ | ||||
|               if(!hideTextMore) | ||||
|                 TextSpan( | ||||
|                   text: textData, | ||||
|                   style: const TextStyle( | ||||
|                       color: ColorPalette.primary, | ||||
|                       decoration: TextDecoration.underline, | ||||
|                   ), | ||||
|                 ), | ||||
|               if(!hideTextMore && !hideIconMore) | ||||
|                 const WidgetSpan( | ||||
|                   child: SizedBox( | ||||
|                     width: 3.0, | ||||
|                   ), | ||||
|                 ), | ||||
|               if(!hideIconMore) | ||||
|                 WidgetSpan( | ||||
|                   child: Icon( | ||||
|                     (type == 1) | ||||
|                         ? Icons.keyboard_arrow_down | ||||
|                         : Icons.keyboard_arrow_up, | ||||
|                     color: ColorPalette.slate300, | ||||
|                     size: 28, | ||||
|                   ), | ||||
|                 ), | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -84,7 +84,7 @@ class NumericPad extends StatelessWidget { | ||||
|   } | ||||
|  | ||||
|   Widget spaceWidget() { | ||||
|     return Expanded( | ||||
|     return const Expanded( | ||||
|         child: SizedBox() | ||||
|     ); | ||||
|   } | ||||
| @@ -96,13 +96,15 @@ class NumericPad extends StatelessWidget { | ||||
|             onNumberSelected(number); | ||||
|           }, | ||||
|           child: Container( | ||||
|             color: Colors.transparent, | ||||
|             padding: EdgeInsets.symmetric(vertical: SizeConfig.height * .028), | ||||
|             child: Text( | ||||
|               number, | ||||
|               textAlign: TextAlign.center, | ||||
|               style: TextStyle( | ||||
|                   fontSize: 28, | ||||
|                   fontWeight: FontWeight.bold | ||||
|               style: const TextStyle( | ||||
|                 fontSize: 28, | ||||
|                 fontWeight: FontWeight.bold, | ||||
|                 color: ColorPalette.slate800 | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
| @@ -116,9 +118,14 @@ class NumericPad extends StatelessWidget { | ||||
|           onTap: () { | ||||
|             onNumberSelected(''); | ||||
|           }, | ||||
|           child: Icon( | ||||
|             Icons.highlight_remove, | ||||
|             size: 28, | ||||
|           child: Container( | ||||
|             color: Colors.transparent, | ||||
|             padding: EdgeInsets.symmetric(vertical: SizeConfig.height * .028), | ||||
|             child: const Icon( | ||||
|               Icons.backspace_outlined, | ||||
|               size: 28, | ||||
|               color: ColorPalette.slate800, | ||||
|             ), | ||||
|           ), | ||||
|         ) | ||||
|     ); | ||||
|   | ||||
							
								
								
									
										57
									
								
								lib/application/component/radio_agreement.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,57 @@ | ||||
| import 'package:cims_apps/application/component/expandable_widget/expandable_widget.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class RadioAgreement extends StatelessWidget { | ||||
|   final void Function() onTap; | ||||
|   final bool isAgree; | ||||
|   final String desc; | ||||
|   const RadioAgreement({super.key, required this.isAgree, required this.desc, required this.onTap,}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Padding( | ||||
|       padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|       child: Row( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           GestureDetector( | ||||
|             onTap: onTap, | ||||
|             child: AnimatedContainer( | ||||
|               margin: const EdgeInsets.only(top: 4), | ||||
|               duration: const Duration(milliseconds: 200), | ||||
|               height: 16, | ||||
|               width: 16, | ||||
|               padding: const EdgeInsets.all(1), | ||||
|               alignment: Alignment.center, | ||||
|               decoration: BoxDecoration( | ||||
|                   shape: BoxShape.circle, | ||||
|                   border: Border.all( | ||||
|                       color: isAgree | ||||
|                           ? ColorPalette.primary | ||||
|                           : ColorPalette.slate200)), | ||||
|               child: AnimatedContainer( | ||||
|                 duration: const Duration(milliseconds: 200), | ||||
|                 child: Container( | ||||
|                   decoration: BoxDecoration( | ||||
|                       color: | ||||
|                       isAgree ? ColorPalette.primary : ColorPalette.white, | ||||
|                       shape: BoxShape.circle), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|           const SizedBox( | ||||
|             width: 12, | ||||
|           ), | ||||
|           Expanded( | ||||
|             child: ExpandableWidget( | ||||
|               content: desc, | ||||
|               maxLinesToShow: 3, | ||||
|             ) | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,4 +1,5 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/expandable_widget/expandable_widget.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/risk_profile/risk_profile_view_model/risk_profile_view_model.dart'; | ||||
| @@ -73,20 +74,20 @@ class RiskProfile extends StatelessWidget { | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 Container( | ||||
|                   padding: EdgeInsets.all(24), | ||||
|                   padding: const EdgeInsets.all(24), | ||||
|                   child: Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       Text( | ||||
|                         riskProfile.type, | ||||
|                         style: TextStyle( | ||||
|                         style: const TextStyle( | ||||
|                             fontWeight: FontWeight.bold, | ||||
|                             fontSize: 24, | ||||
|                             color: ColorPalette.white | ||||
|                         ), | ||||
|                       ), | ||||
|                       SizedBox(height: 16,), | ||||
|                       Text('Total Score :', | ||||
|                       const SizedBox(height: 16,), | ||||
|                       const Text('Total Score :', | ||||
|                         style: TextStyle( | ||||
|                             fontWeight: FontWeight.bold, | ||||
|                             fontSize: 16, | ||||
| @@ -94,7 +95,7 @@ class RiskProfile extends StatelessWidget { | ||||
|                         ), | ||||
|                       ), | ||||
|                       Text('$totalScore', | ||||
|                         style: TextStyle( | ||||
|                         style: const TextStyle( | ||||
|                             fontWeight: FontWeight.bold, | ||||
|                             fontSize: 28, | ||||
|                             color: ColorPalette.white | ||||
| @@ -107,20 +108,20 @@ class RiskProfile extends StatelessWidget { | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         SizedBox( | ||||
|         const SizedBox( | ||||
|           height: 24, | ||||
|         ), | ||||
|         Text( | ||||
|             riskProfile.desc, | ||||
|             style: TextStyle( | ||||
|                 color: ColorPalette.slate500, | ||||
|                 fontSize: 16 | ||||
|             ) | ||||
|         ExpandableWidget( | ||||
|           content: riskProfile.desc, | ||||
|           hideTextMore: true, | ||||
|           hideIconMore: false, | ||||
|           maxLinesToShow: 4, | ||||
|           alignmentMore: Alignment.center, | ||||
|         ), | ||||
|         SizedBox( | ||||
|         const SizedBox( | ||||
|           height: 24, | ||||
|         ), | ||||
|         Text( | ||||
|         const Text( | ||||
|           'Suitable Product', | ||||
|           style: TextStyle( | ||||
|               color: ColorPalette.slate800, | ||||
| @@ -128,7 +129,7 @@ class RiskProfile extends StatelessWidget { | ||||
|               fontSize: 18 | ||||
|           ), | ||||
|         ), | ||||
|         SizedBox( | ||||
|         const SizedBox( | ||||
|           height: 16, | ||||
|         ), | ||||
|         rowSuitableProduct ? | ||||
| @@ -137,7 +138,7 @@ class RiskProfile extends StatelessWidget { | ||||
|               return Expanded( | ||||
|                 child: Container( | ||||
|                   margin: EdgeInsets.only(left: e.key != 0 ? 12 : 0), | ||||
|                   padding: EdgeInsets.all(16), | ||||
|                   padding: const EdgeInsets.all(16), | ||||
|                   decoration: BoxDecoration( | ||||
|                     border: Border.all(color: ColorPalette.slate200), | ||||
|                     borderRadius: BorderRadius.circular(6) | ||||
| @@ -146,18 +147,18 @@ class RiskProfile extends StatelessWidget { | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       Container( | ||||
|                           padding: EdgeInsets.all(8), | ||||
|                           padding: const EdgeInsets.all(8), | ||||
|                           decoration: BoxDecoration( | ||||
|                               shape: BoxShape.circle, | ||||
|                               color: riskProfile.color.withOpacity(0.1) | ||||
|                           ), | ||||
|                           child: Image.asset(e.value['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color) | ||||
|                       ), | ||||
|                       SizedBox( | ||||
|                       const SizedBox( | ||||
|                         height: 12, | ||||
|                       ), | ||||
|                       Text(e.value['desc'], | ||||
|                         style: TextStyle( | ||||
|                         style: const TextStyle( | ||||
|                             fontSize: 16, | ||||
|                             fontWeight: FontWeight.bold, | ||||
|                             color: ColorPalette.slate800 | ||||
| @@ -173,7 +174,7 @@ class RiskProfile extends StatelessWidget { | ||||
|           runSpacing: 16, | ||||
|           children: riskProfile.suitableProduct.map((e) { | ||||
|             return Container( | ||||
|               padding: EdgeInsets.all(16), | ||||
|               padding: const EdgeInsets.all(16), | ||||
|               decoration: BoxDecoration( | ||||
|                 borderRadius: BorderRadius.circular(6), | ||||
|                 border: Border.all(color: ColorPalette.slate200), | ||||
| @@ -181,7 +182,7 @@ class RiskProfile extends StatelessWidget { | ||||
|               child: Row( | ||||
|                 children: [ | ||||
|                   Container( | ||||
|                       padding: EdgeInsets.all(8), | ||||
|                       padding: const EdgeInsets.all(8), | ||||
|                       alignment: Alignment.center, | ||||
|                       decoration: BoxDecoration( | ||||
|                           shape: BoxShape.circle, | ||||
| @@ -189,12 +190,12 @@ class RiskProfile extends StatelessWidget { | ||||
|                       ), | ||||
|                       child: Image.asset(e['icon'], width: SizeConfig.width * 0.07, color: riskProfile.color) | ||||
|                   ), | ||||
|                   SizedBox( | ||||
|                   const SizedBox( | ||||
|                     width: 12, | ||||
|                   ), | ||||
|                   Expanded( | ||||
|                     child: Text(e['desc'], | ||||
|                       style: TextStyle( | ||||
|                       style: const TextStyle( | ||||
|                           fontSize: 18, | ||||
|                           fontWeight: FontWeight.bold, | ||||
|                           color: ColorPalette.slate800 | ||||
|   | ||||
| @@ -47,6 +47,7 @@ class SelectFormView extends StatelessWidget { | ||||
|     bottomSheet() { | ||||
|       showModalBottomSheet<void>( | ||||
|         context: context, | ||||
|         isDismissible: false, | ||||
|         shape: RoundedRectangleBorder( | ||||
|           borderRadius: BorderRadius.only( | ||||
|             topLeft: _borderRadius, | ||||
| @@ -82,7 +83,10 @@ class SelectFormView extends StatelessWidget { | ||||
|                         ), | ||||
|                       ), | ||||
|                       IconButton( | ||||
|                           onPressed: () => Navigator.pop(context), | ||||
|                           onPressed: () { | ||||
|                             ctrl?.clear(); | ||||
|                             Navigator.pop(context); | ||||
|                           }, | ||||
|                           icon: const Icon( | ||||
|                             Icons.clear, | ||||
|                             size: 26, | ||||
| @@ -134,7 +138,7 @@ class SelectFormView extends StatelessWidget { | ||||
|                                 onTap: () { | ||||
|                                   stateSetter(() { | ||||
|                                     ctrl?.text = e.text; | ||||
|                                     onSelect(e.key); | ||||
|                                     onSelect(e.text); | ||||
|                                   }); | ||||
|                                 }, | ||||
|                               ), | ||||
| @@ -148,7 +152,9 @@ class SelectFormView extends StatelessWidget { | ||||
|                     name: 'Select', | ||||
|                     marginVertical: 4.0, | ||||
|                     onPressed: () { | ||||
|                       Navigator.pop(context); | ||||
|                       if (ctrl!.text.isNotEmpty) { | ||||
|                         Navigator.pop(context); | ||||
|                       } | ||||
|                     }, | ||||
|                   ) | ||||
|                 ], | ||||
|   | ||||
| @@ -36,8 +36,8 @@ class GoalInvestingView extends StatelessWidget { | ||||
|                   routePush( | ||||
|                     context, | ||||
|                     page: OtherPlanView( | ||||
|                       selectedPlan: (value) { | ||||
|                         onListSelected(e.value.title); | ||||
|                       selectedPlan: (val) { | ||||
|                         onListSelected(val); | ||||
|                       }, | ||||
|                     ) | ||||
|                   ); | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| @@ -6,9 +8,13 @@ import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class InputInvestmentView extends StatefulWidget { | ||||
|   final String selectedPlan; | ||||
|   final String? currentPlan; | ||||
|   final void Function()? changePlan; | ||||
|   final int? currentPrice; | ||||
|   final int? minimumPrice; | ||||
|   final int? maximumPrice; | ||||
|   final void Function(String value) nextMove; | ||||
|   const InputInvestmentView({super.key, required this.selectedPlan, required this.nextMove}); | ||||
|   const InputInvestmentView({super.key, required this.nextMove, this.currentPlan, this.minimumPrice, this.maximumPrice, this.currentPrice, this.changePlan}); | ||||
|  | ||||
|   @override | ||||
|   State<InputInvestmentView> createState() => _InputInvestmentViewState(); | ||||
| @@ -17,10 +23,35 @@ class InputInvestmentView extends StatefulWidget { | ||||
| class _InputInvestmentViewState extends State<InputInvestmentView> { | ||||
|   TextEditingController inputController = TextEditingController(); | ||||
|  | ||||
|   void validationInputValue(String currentValue) { | ||||
|     currentValue = currentValue.replaceAll('Rp ', '').replaceAll('.', ''); | ||||
|     if(currentValue.isEmpty){ | ||||
|       currentValue = '0'; | ||||
|     } | ||||
|     double parseValue = double.parse(currentValue); | ||||
|     if(widget.minimumPrice != null){ | ||||
|       if(parseValue <= widget.minimumPrice!){ | ||||
|         parseValue = widget.minimumPrice!.toDouble(); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if(widget.maximumPrice != null){ | ||||
|       if(parseValue >= widget.maximumPrice!){ | ||||
|         parseValue = widget.maximumPrice!.toDouble(); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     inputController.text = NumberFormatter.numberCurrency(parseValue, 'Rp ', 'id_ID', decimalDigits: 0); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     // TODO: implement initState | ||||
|     inputController.text = 'Rp 0'; | ||||
|     if(widget.currentPrice != null){ | ||||
|       inputController.text = NumberFormatter.numberCurrency(widget.currentPrice, 'Rp ', 'id_ID', decimalDigits: 0); | ||||
|     }else{ | ||||
|       inputController.text = 'Rp 0'; | ||||
|     } | ||||
|     super.initState(); | ||||
|   } | ||||
|  | ||||
| @@ -41,56 +72,53 @@ class _InputInvestmentViewState extends State<InputInvestmentView> { | ||||
|       child: Column( | ||||
|         mainAxisSize: MainAxisSize.min, | ||||
|         children: [ | ||||
|           SizedBox(height: 16), | ||||
|           Padding( | ||||
|             padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), | ||||
|             padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), | ||||
|             child: Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     Text(widget.selectedPlan, | ||||
|                       style: TextStyle( | ||||
|                         fontSize: 20, | ||||
|                         fontWeight: FontWeight.w700, | ||||
|  | ||||
|                 if(widget.currentPlan != null) | ||||
|                   Row( | ||||
|                     mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                     children: [ | ||||
|                       Text(widget.currentPlan ?? '', | ||||
|                         style: const TextStyle( | ||||
|                           fontSize: 20, | ||||
|                           fontWeight: FontWeight.w700, | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                     Row( | ||||
|                       children: [ | ||||
|                         Icon(Icons.change_circle_outlined, color: ColorPalette.primary, size: 20), | ||||
|                         SizedBox(width: 4), | ||||
|                         Text('Change', | ||||
|                           style: TextStyle( | ||||
|                               fontSize: 16, | ||||
|                               fontWeight: FontWeight.w600, | ||||
|                               color: ColorPalette.primary | ||||
|                           ), | ||||
|                         ) | ||||
|                       ], | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|                       InkWell( | ||||
|                         borderRadius: BorderRadius.circular(16), | ||||
|                         onTap: widget.changePlan, | ||||
|                         child: const Row( | ||||
|                           children: [ | ||||
|                             Icon(Icons.change_circle_outlined, color: ColorPalette.primary, size: 20), | ||||
|                             SizedBox(width: 4), | ||||
|                             Text('Change', | ||||
|                               style: TextStyle( | ||||
|                                   fontSize: 16, | ||||
|                                   fontWeight: FontWeight.w600, | ||||
|                                   color: ColorPalette.primary | ||||
|                               ), | ||||
|                             ) | ||||
|                           ], | ||||
|                         ), | ||||
|                       ) | ||||
|                     ], | ||||
|                   ), | ||||
|                 TextField( | ||||
|                   controller: inputController, | ||||
|                   textAlign: TextAlign.center, | ||||
|                   style: TextStyle( | ||||
|                   style: const TextStyle( | ||||
|                       fontSize: 28, | ||||
|                       fontWeight: FontWeight.w600, | ||||
|                       color: ColorPalette.slate800 | ||||
|                   ), | ||||
|                   keyboardType: TextInputType.number, | ||||
|                   onChanged: (value) { | ||||
|                     value = value.replaceAll('Rp ', '').replaceAll('.', ''); | ||||
|                     double parseValue = double.parse(value); | ||||
|                     if(value.isNotEmpty){ | ||||
|                       inputController.text = NumberFormatter.numberCurrency(parseValue, 'Rp ', 'id_ID', decimalDigits: 0); | ||||
|                     }else{ | ||||
|                       inputController.text = NumberFormatter.numberCurrency(0, 'Rp ', 'id_ID', decimalDigits: 0); | ||||
|                     } | ||||
|                     validationInputValue(value); | ||||
|                   }, | ||||
|                   decoration: InputDecoration( | ||||
|                   decoration: const InputDecoration( | ||||
|                       enabledBorder: UnderlineInputBorder( | ||||
|                         borderSide: BorderSide( | ||||
|                             color: ColorPalette.primary, | ||||
| @@ -99,30 +127,36 @@ class _InputInvestmentViewState extends State<InputInvestmentView> { | ||||
|                       ) | ||||
|                   ), | ||||
|                 ), | ||||
|                 SizedBox(height: 12), | ||||
|                 Text('Minimum Budget Rp1,000,000', | ||||
|                   style: TextStyle( | ||||
|                       color: ColorPalette.slate400, | ||||
|                       fontSize: 16, | ||||
|                       fontWeight: FontWeight.w600 | ||||
|                 const SizedBox(height: 12), | ||||
|                 if(widget.minimumPrice != null) | ||||
|                   Text('Minimum ${NumberFormatter.numberCurrency(widget.minimumPrice, 'Rp ', 'id_ID')}', | ||||
|                     style: const TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontSize: 16, | ||||
|                         fontWeight: FontWeight.w600 | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|                 SizedBox(height: 16), | ||||
|                 if(widget.maximumPrice != null) | ||||
|                   Text('Maximum ${NumberFormatter.numberCurrency(widget.maximumPrice, 'Rp ', 'id_ID')}', | ||||
|                     style: const TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontSize: 16, | ||||
|                         fontWeight: FontWeight.w600 | ||||
|                     ), | ||||
|                   ), | ||||
|                 const SizedBox(height: 16), | ||||
|                 NumericPad(onNumberSelected: (p0) { | ||||
|                   String checkIsZeroInput = inputController.text.replaceAll('Rp ', '').replaceAll(',', ''); | ||||
|                   String getNumeric = p0; | ||||
|                   String currentValue = inputController.text; | ||||
|                   if(p0.isNotEmpty){ | ||||
|                     if(checkIsZeroInput != '0'){ | ||||
|                       getNumeric = checkIsZeroInput + getNumeric; | ||||
|                     if(currentValue != '0'){ | ||||
|                       currentValue = currentValue + p0; | ||||
|                     } | ||||
|                   }else{ | ||||
|                     getNumeric = checkIsZeroInput.substring(0, checkIsZeroInput.length - 1); | ||||
|                     currentValue = currentValue.substring(0, currentValue.length - 1); | ||||
|                   } | ||||
|                   String formatNumeric = NumberFormatter.numberCurrency( | ||||
|                       double.parse(getNumeric), 'Rp ', 'id_ID', decimalDigits: 0).replaceAll('.', ','); | ||||
|                   inputController.text = formatNumeric; | ||||
|                   validationInputValue(currentValue); | ||||
|                 }), | ||||
|                 SizedBox(height: 8), | ||||
|                 const SizedBox(height: 24), | ||||
|                 ButtonView( | ||||
|                   name: 'Next', | ||||
|                   onPressed: () { | ||||
| @@ -130,7 +164,7 @@ class _InputInvestmentViewState extends State<InputInvestmentView> { | ||||
|                   }, | ||||
|                   width: SizeConfig.width, | ||||
|                   heightWrapContent: true, | ||||
|                   contentPadding: EdgeInsets.symmetric(vertical: 16), | ||||
|                   contentPadding: const EdgeInsets.symmetric(vertical: 16), | ||||
|                   marginVertical: 0, | ||||
|                 ) | ||||
|               ], | ||||
| @@ -138,6 +172,6 @@ class _InputInvestmentViewState extends State<InputInvestmentView> { | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     );; | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart'; | ||||
| @@ -72,6 +74,8 @@ class _OtherPlanViewState extends State<OtherPlanView> { | ||||
|           disabled: !(selectedPlan.img != ''), | ||||
|           onPressed: () { | ||||
|             Navigator.pop(context); | ||||
|             print('haloo'); | ||||
|             print(selectedPlan.name); | ||||
|             widget.selectedPlan(selectedPlan.name); | ||||
|           }, | ||||
|           heightWrapContent: true, | ||||
| @@ -91,6 +95,7 @@ class _OtherPlanViewState extends State<OtherPlanView> { | ||||
|         if(plan.name == 'Create Plan'){ | ||||
|           showModalBottomSheet( | ||||
|             context: context, | ||||
|             isDismissible: false, | ||||
|             builder: (context) => modalCreatePlan(), | ||||
|           ); | ||||
|         } | ||||
| @@ -150,6 +155,9 @@ class _OtherPlanViewState extends State<OtherPlanView> { | ||||
|               GestureDetector( | ||||
|                 onTap: () { | ||||
|                   Navigator.pop(context); | ||||
|                   setState(() { | ||||
|                     selectedPlan = Plan('', ''); | ||||
|                   }); | ||||
|                 }, | ||||
|                 child: Icon(Icons.close_rounded), | ||||
|               ) | ||||
| @@ -161,12 +169,12 @@ class _OtherPlanViewState extends State<OtherPlanView> { | ||||
|           ), | ||||
|           SizedBox(height: 24), | ||||
|           ButtonView( | ||||
|             name: 'Select', | ||||
|             name: 'Next', | ||||
|             marginVertical: 0, | ||||
|             disabled: !(createController.text != ''), | ||||
|             onPressed: () { | ||||
|               Navigator.of(context)..pop()..pop(); | ||||
|               widget.selectedPlan(selectedPlan.name); | ||||
|               widget.selectedPlan(createController.text); | ||||
|             }, | ||||
|             heightWrapContent: true, | ||||
|             width: SizeConfig.width, | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/radio_agreement.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/route/route.dart'; | ||||
| import 'package:cims_apps/core/utils/number_formatter.dart'; | ||||
| @@ -6,249 +7,249 @@ import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class TotalPaymentView extends StatefulWidget { | ||||
| class TotalPaymentView extends StatelessWidget { | ||||
|   final int totalInvest; | ||||
|   final List<Product> listProduct; | ||||
|   final bool isAgree; | ||||
|   final void Function() onTapAgree; | ||||
|   const TotalPaymentView({ | ||||
|     super.key, | ||||
|     required this.listProduct, | ||||
|     required this.totalInvest, | ||||
|     required this.isAgree, | ||||
|     required this.onTapAgree, | ||||
|   }); | ||||
|  | ||||
|   @override | ||||
|   State<TotalPaymentView> createState() => _TotalPaymentViewState(); | ||||
| } | ||||
|  | ||||
| class _TotalPaymentViewState extends State<TotalPaymentView> { | ||||
|   bool isAgreement = false; | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       decoration: BoxDecoration( | ||||
|           color: Colors.white, | ||||
|           borderRadius: BorderRadius.circular(16) | ||||
|       ), | ||||
|       child: Column( | ||||
|         mainAxisSize: MainAxisSize.min, | ||||
|         children: [ | ||||
|           Padding( | ||||
|             padding: const EdgeInsets.all(24.0), | ||||
|             child: Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 const Text('Your Investment Today', | ||||
|                   style: TextStyle( | ||||
|                       color: ColorPalette.slate800, | ||||
|                       fontWeight: FontWeight.w600, | ||||
|                       fontSize: 16 | ||||
|                   ), | ||||
|                 ), | ||||
|                 GestureDetector( | ||||
|                     onTap: () => Navigator.pop(context), | ||||
|                     child: const Icon(Icons.close_rounded) | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           ...widget.listProduct.asMap().entries.map((e) { | ||||
|             return Container( | ||||
|               padding: const EdgeInsets.only(left: 16, right: 24, bottom: 16, top: 16), | ||||
|               decoration: BoxDecoration( | ||||
|                   border: Border( | ||||
|                       left: BorderSide( | ||||
|                         width: 8, | ||||
|                         color: ColorPalette.investTypeColor[e.value.type]! | ||||
|                       ) | ||||
|                   ) | ||||
|               ), | ||||
|     return SingleChildScrollView( | ||||
|       child: Container( | ||||
|         decoration: BoxDecoration( | ||||
|             color: Colors.white, borderRadius: BorderRadius.circular(16)), | ||||
|         child: Column( | ||||
|           mainAxisSize: MainAxisSize.min, | ||||
|           children: [ | ||||
|             Padding( | ||||
|               padding: const EdgeInsets.all(24.0), | ||||
|               child: Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   Expanded( | ||||
|                       flex: 5, | ||||
|                       child: Text(e.value.name ?? '', | ||||
|                         style: const TextStyle( | ||||
|                             color: ColorPalette.slate400, | ||||
|                             fontWeight: FontWeight.w600, | ||||
|                             fontSize: 16 | ||||
|                         ), | ||||
|                       ) | ||||
|                   ), | ||||
|                   Expanded( | ||||
|                       flex: 7, | ||||
|                       child: Text( | ||||
|                         NumberFormatter.numberCurrency(widget.totalInvest * e.value.totalPercent!, 'Rp ', 'id_ID'), | ||||
|                         textAlign: TextAlign.end, | ||||
|                         style: const TextStyle( | ||||
|                             fontWeight: FontWeight.w700, | ||||
|                             fontSize: 18, | ||||
|                             color: ColorPalette.slate800 | ||||
|                         ), | ||||
|                       ) | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|             ); | ||||
|           }).toList(), | ||||
|           const SizedBox(height: 16), | ||||
|           const Padding( | ||||
|             padding: EdgeInsets.symmetric(horizontal: 24), | ||||
|             child: Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 Text('Purchase Commission', | ||||
|                   style: TextStyle( | ||||
|                       color: ColorPalette.slate400, | ||||
|                       fontWeight: FontWeight.w600, | ||||
|                       fontSize: 16 | ||||
|                   ), | ||||
|                 ), | ||||
|                 Text('Free', | ||||
|                   style: TextStyle( | ||||
|                       color: ColorPalette.primary, | ||||
|                       fontSize: 18, | ||||
|                       fontWeight: FontWeight.w700 | ||||
|                   ), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           const SizedBox(height: 16,), | ||||
|           Container( | ||||
|             color: ColorPalette.slate200.withOpacity(0.5), | ||||
|             padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|             child: Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 const Text('Total', | ||||
|                   style: TextStyle( | ||||
|                       color: ColorPalette.slate400, | ||||
|                       fontWeight: FontWeight.w600, | ||||
|                       fontSize: 16 | ||||
|                   ), | ||||
|                 ), | ||||
|                 Text(NumberFormatter.numberCurrency(widget.totalInvest, 'Rp ', 'id_ID'), | ||||
|                   textAlign: TextAlign.end, | ||||
|                   style: const TextStyle( | ||||
|                       fontWeight: FontWeight.w700, | ||||
|                       fontSize: 18, | ||||
|                       color: ColorPalette.slate800 | ||||
|                   ), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           buttonAgreement(), | ||||
|           Container( | ||||
|             padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|             child: Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 const Text('Total Payment', | ||||
|                   style: TextStyle( | ||||
|                       color: ColorPalette.slate400, | ||||
|                       fontWeight: FontWeight.w600, | ||||
|                       fontSize: 16 | ||||
|                   ), | ||||
|                 ), | ||||
|                 Text(NumberFormatter.numberCurrency(widget.totalInvest, 'Rp ', 'id_ID'), | ||||
|                   textAlign: TextAlign.end, | ||||
|                   style: const TextStyle( | ||||
|                       fontWeight: FontWeight.w700, | ||||
|                       fontSize: 18, | ||||
|                       color: ColorPalette.slate800 | ||||
|                   ), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           ButtonView( | ||||
|             disabled: !isAgreement, | ||||
|             name: 'Subscribe Now', | ||||
|             onPressed: () { | ||||
|               routePush(context, page: PaymentMethodView( | ||||
|                 totalInvest: widget.totalInvest, | ||||
|               )); | ||||
|             }, | ||||
|             disabledBgColor: ColorPalette.slate200.withOpacity(0.5), | ||||
|             textColor: isAgreement ? Colors.white : ColorPalette.slate400, | ||||
|             textWeight: FontWeight.w700, | ||||
|             textSize: 20, | ||||
|             backgroundColor: ColorPalette.primary, | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget buttonAgreement() { | ||||
|     bool isAgree = isAgreement; | ||||
|     return Padding( | ||||
|       padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|       child: Row( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           GestureDetector( | ||||
|             onTap: () { | ||||
|               setState(() { | ||||
|                 isAgreement = !isAgreement; | ||||
|               }); | ||||
|             }, | ||||
|             child: AnimatedContainer( | ||||
|               margin: const EdgeInsets.only(top: 4), | ||||
|               duration: const Duration(milliseconds: 200), | ||||
|               height: 16, | ||||
|               width: 16, | ||||
|               padding: const EdgeInsets.all(1), | ||||
|               alignment: Alignment.center, | ||||
|               decoration: BoxDecoration( | ||||
|                   shape: BoxShape.circle, | ||||
|                   border: Border.all( | ||||
|                       color: isAgree ? ColorPalette.primary : ColorPalette.slate200 | ||||
|                   ) | ||||
|               ), | ||||
|               child: AnimatedContainer( | ||||
|                 duration: const Duration(milliseconds: 200), | ||||
|                 child: Container( | ||||
|                   decoration: BoxDecoration( | ||||
|                       color: isAgree ? ColorPalette.primary : ColorPalette.white, | ||||
|                       shape: BoxShape.circle | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|           const SizedBox(width: 12,), | ||||
|           Expanded( | ||||
|               child: Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   const Text('I agree to buy the mutual fund on this page and have read and agreed to all the contents of the Prospectus and summary information and understand the risks of my investment decision.', | ||||
|                   const Text( | ||||
|                     'Your Investment Today', | ||||
|                     style: TextStyle( | ||||
|                         fontSize: 16, | ||||
|                         color: ColorPalette.slate800, | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         color: ColorPalette.slate400 | ||||
|                     ), | ||||
|                         fontSize: 16), | ||||
|                   ), | ||||
|                   GestureDetector( | ||||
|                       onTap: () { | ||||
|  | ||||
|                       }, | ||||
|                       child: const Text('Read More', | ||||
|                         style: TextStyle( | ||||
|                             fontSize: 16, | ||||
|                             fontWeight: FontWeight.w600, | ||||
|                             decoration: TextDecoration.underline, | ||||
|                             color: ColorPalette.primary | ||||
|                         ), | ||||
|                       ) | ||||
|                       onTap: () => Navigator.pop(context), | ||||
|                       child: const Icon(Icons.close_rounded)) | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|             ...listProduct.asMap().entries.map((e) { | ||||
|               return Container( | ||||
|                 padding: const EdgeInsets.only( | ||||
|                     left: 16, right: 24, bottom: 16, top: 16), | ||||
|                 decoration: BoxDecoration( | ||||
|                     border: Border( | ||||
|                         left: BorderSide( | ||||
|                             width: 8, | ||||
|                             color: | ||||
|                                 ColorPalette.investTypeColor[e.value.type]!))), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     Expanded( | ||||
|                         flex: 5, | ||||
|                         child: Text( | ||||
|                           e.value.name ?? '', | ||||
|                           style: const TextStyle( | ||||
|                               color: ColorPalette.slate400, | ||||
|                               fontWeight: FontWeight.w600, | ||||
|                               fontSize: 16), | ||||
|                         )), | ||||
|                     Expanded( | ||||
|                         flex: 7, | ||||
|                         child: Text( | ||||
|                           NumberFormatter.numberCurrency( | ||||
|                               totalInvest * e.value.totalPercent!, | ||||
|                               'Rp ', | ||||
|                               'id_ID'), | ||||
|                           textAlign: TextAlign.end, | ||||
|                           style: const TextStyle( | ||||
|                               fontWeight: FontWeight.w700, | ||||
|                               fontSize: 18, | ||||
|                               color: ColorPalette.slate800), | ||||
|                         )) | ||||
|                   ], | ||||
|                 ), | ||||
|               ); | ||||
|             }).toList(), | ||||
|             const SizedBox(height: 16), | ||||
|             const Padding( | ||||
|               padding: EdgeInsets.symmetric(horizontal: 24), | ||||
|               child: Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   Text( | ||||
|                     'Purchase Commission', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         fontSize: 16), | ||||
|                   ), | ||||
|                   Text( | ||||
|                     'Free', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.primary, | ||||
|                         fontSize: 18, | ||||
|                         fontWeight: FontWeight.w700), | ||||
|                   ) | ||||
|                 ], | ||||
|               ) | ||||
|           ) | ||||
|         ], | ||||
|               ), | ||||
|             ), | ||||
|             const SizedBox( | ||||
|               height: 16, | ||||
|             ), | ||||
|             Container( | ||||
|               color: ColorPalette.slate200.withOpacity(0.5), | ||||
|               padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|               child: Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   const Text( | ||||
|                     'Total', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         fontSize: 16), | ||||
|                   ), | ||||
|                   Text( | ||||
|                     NumberFormatter.numberCurrency( | ||||
|                         totalInvest, 'Rp ', 'id_ID'), | ||||
|                     textAlign: TextAlign.end, | ||||
|                     style: const TextStyle( | ||||
|                         fontWeight: FontWeight.w700, | ||||
|                         fontSize: 18, | ||||
|                         color: ColorPalette.slate800), | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|             RadioAgreement( | ||||
|               isAgree: isAgree, | ||||
|               desc: 'I agree to buy the mutual fund on this page and have read and agreed to all the contents of the Prospectus and summary information and understand the risks of my investment decision.', | ||||
|               onTap: () { | ||||
|                 onTapAgree(); | ||||
|               }, | ||||
|             ), | ||||
|             Container( | ||||
|               padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|               child: Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   const Text( | ||||
|                     'Total Payment', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         fontSize: 16), | ||||
|                   ), | ||||
|                   Text( | ||||
|                     NumberFormatter.numberCurrency( | ||||
|                         totalInvest, 'Rp ', 'id_ID'), | ||||
|                     textAlign: TextAlign.end, | ||||
|                     style: const TextStyle( | ||||
|                         fontWeight: FontWeight.w700, | ||||
|                         fontSize: 18, | ||||
|                         color: ColorPalette.slate800), | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|             ButtonView( | ||||
|               disabled: !isAgree, | ||||
|               name: 'Subscribe Now', | ||||
|               onPressed: () { | ||||
|                 routePush(context, | ||||
|                     page: PaymentMethodView( | ||||
|                       totalInvest: totalInvest, | ||||
|                     )); | ||||
|               }, | ||||
|               disabledBgColor: ColorPalette.slate200.withOpacity(0.5), | ||||
|               textColor: isAgree ? Colors.white : ColorPalette.slate400, | ||||
|               textWeight: FontWeight.w700, | ||||
|               textSize: 20, | ||||
|               backgroundColor: ColorPalette.primary, | ||||
|             ) | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   // Widget buttonAgreement() { | ||||
|   //   return Padding( | ||||
|   //     padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|   //     child: Row( | ||||
|   //       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|   //       children: [ | ||||
|   //         GestureDetector( | ||||
|   //           onTap: () { | ||||
|   //           }, | ||||
|   //           child: AnimatedContainer( | ||||
|   //             margin: const EdgeInsets.only(top: 4), | ||||
|   //             duration: const Duration(milliseconds: 200), | ||||
|   //             height: 16, | ||||
|   //             width: 16, | ||||
|   //             padding: const EdgeInsets.all(1), | ||||
|   //             alignment: Alignment.center, | ||||
|   //             decoration: BoxDecoration( | ||||
|   //                 shape: BoxShape.circle, | ||||
|   //                 border: Border.all( | ||||
|   //                     color: isAgree | ||||
|   //                         ? ColorPalette.primary | ||||
|   //                         : ColorPalette.slate200)), | ||||
|   //             child: AnimatedContainer( | ||||
|   //               duration: const Duration(milliseconds: 200), | ||||
|   //               child: Container( | ||||
|   //                 decoration: BoxDecoration( | ||||
|   //                     color: | ||||
|   //                         isAgree ? ColorPalette.primary : ColorPalette.white, | ||||
|   //                     shape: BoxShape.circle), | ||||
|   //               ), | ||||
|   //             ), | ||||
|   //           ), | ||||
|   //         ), | ||||
|   //         const SizedBox( | ||||
|   //           width: 12, | ||||
|   //         ), | ||||
|   //         Expanded( | ||||
|   //             child: Column( | ||||
|   //           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|   //           children: [ | ||||
|   //             const Text( | ||||
|   //               'I agree to buy the mutual fund on this page and have read and agreed to all the contents of the Prospectus and summary information and understand the risks of my investment decision.', | ||||
|   //               style: TextStyle( | ||||
|   //                   fontSize: 16, | ||||
|   //                   fontWeight: FontWeight.w600, | ||||
|   //                   color: ColorPalette.slate400), | ||||
|   //             ), | ||||
|   //             GestureDetector( | ||||
|   //                 onTap: () {}, | ||||
|   //                 child: const Text( | ||||
|   //                   'Read More', | ||||
|   //                   style: TextStyle( | ||||
|   //                       fontSize: 16, | ||||
|   //                       fontWeight: FontWeight.w600, | ||||
|   //                       decoration: TextDecoration.underline, | ||||
|   //                       color: ColorPalette.primary), | ||||
|   //                 )) | ||||
|   //           ], | ||||
|   //         )) | ||||
|   //       ], | ||||
|   //     ), | ||||
|   //   ); | ||||
|   // } | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import 'dart:io'; | ||||
|  | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/take_picture_screen/take_picture_screen.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| @@ -12,6 +13,7 @@ import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_v | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
| import 'dart:math' as math; | ||||
|  | ||||
| class DisplayPictureScreen extends StatefulWidget { | ||||
|   final String imagePath, content; | ||||
| @@ -93,10 +95,8 @@ class _DisplayPictureScreenState extends State<DisplayPictureScreen> { | ||||
|           return Consumer<SubmissionDataViewModel>( | ||||
|               builder: (context, provider, child) { | ||||
|             return Scaffold( | ||||
|               appBar: AppBar( | ||||
|                 title: const Text('Preview'), | ||||
|                 automaticallyImplyLeading: false, | ||||
|               ), | ||||
|               appBar: CustomAppBar( | ||||
|                   height: SizeConfig.height * .08, title: 'Preview'), | ||||
|               body: Container( | ||||
|                   padding: const EdgeInsets.symmetric(horizontal: 24.0), | ||||
|                   child: Column( | ||||
| @@ -106,7 +106,14 @@ class _DisplayPictureScreenState extends State<DisplayPictureScreen> { | ||||
|                       SizedBox( | ||||
|                           width: SizeConfig.width, | ||||
|                           height: SizeConfig.height * .4, | ||||
|                           child: Image.file(File(widget.imagePath))), | ||||
|                           child: Transform( | ||||
|                               alignment: Alignment.center, | ||||
|                               transform: widget.content == 'selfie' | ||||
|                                   ? Matrix4.rotationY(math.pi) | ||||
|                                   : Matrix4.rotationY(0), | ||||
|                               child: Image.file( | ||||
|                                 File(widget.imagePath), | ||||
|                               ))), | ||||
|                       const Padding( | ||||
|                         padding: EdgeInsets.symmetric(vertical: 16.0), | ||||
|                         child: Text( | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import 'package:camera/camera.dart'; | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/take_picture_screen/display_picture_screen.dart'; | ||||
| import 'package:cims_apps/core/route/route.dart'; | ||||
| @@ -27,7 +28,6 @@ class TakePictureScreenState extends State<TakePictureScreen> { | ||||
|   late String _takeContent; | ||||
|  | ||||
|   Future<void> changeFlash() async { | ||||
|     await _controller.setFlashMode(FlashMode.auto); | ||||
|     setState(() { | ||||
|       isFlash = !isFlash; | ||||
|     }); | ||||
| @@ -44,6 +44,7 @@ class TakePictureScreenState extends State<TakePictureScreen> { | ||||
|     // Next, initialize the controller. This returns a Future. | ||||
|     _initializeControllerFuture = _controller.initialize(); | ||||
|     _takeContent = widget.takeContent; | ||||
|     _controller.setFlashMode(isFlash ? FlashMode.torch : FlashMode.off); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
| @@ -57,9 +58,10 @@ class TakePictureScreenState extends State<TakePictureScreen> { | ||||
|   Widget build(BuildContext context) { | ||||
|     // Fill this out in the next steps. | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         title: const Text('Registration'), | ||||
|         actions: [ | ||||
|       appBar: CustomAppBar( | ||||
|         title: 'Registration', | ||||
|         height: SizeConfig.height * .08, | ||||
|         trailing: [ | ||||
|           IconButton( | ||||
|               onPressed: () { | ||||
|                 changeFlash(); | ||||
|   | ||||
| @@ -141,7 +141,7 @@ class TextFormView extends StatelessWidget { | ||||
|             style: TextStyle( | ||||
|               fontWeight: FontWeight.w500, | ||||
|               fontSize: 14, | ||||
|               color: fontColorDisabled ?? ColorPalette.slate500, | ||||
|               color: fontColorDisabled ?? ColorPalette.slate800, | ||||
|             ), | ||||
|             readOnly: readOnly, | ||||
|             validator: validator, | ||||
| @@ -163,7 +163,7 @@ class TextFormView extends StatelessWidget { | ||||
|                 hintStyle: hintTextStyle ?? | ||||
|                     const TextStyle( | ||||
|                       fontSize: 14, | ||||
|                       color: ColorPalette.greyFont, | ||||
|                       color: ColorPalette.slate400, | ||||
|                       fontWeight: FontWeight.normal, | ||||
|                     ), | ||||
|                 isDense: true, | ||||
| @@ -190,6 +190,18 @@ class TextFormView extends StatelessWidget { | ||||
|                     color: focusedBorderColor ?? ColorPalette.greyBorder, | ||||
|                   ), | ||||
|                 ), | ||||
|                 errorBorder: OutlineInputBorder( | ||||
|                   borderRadius: _borderRadius, | ||||
|                   borderSide: BorderSide( | ||||
|                       color: ColorPalette.red600 | ||||
|                   ) | ||||
|                 ), | ||||
|                 focusedErrorBorder: OutlineInputBorder( | ||||
|                   borderRadius: _borderRadius, | ||||
|                   borderSide: BorderSide( | ||||
|                     color: ColorPalette.red600 | ||||
|                   ) | ||||
|                 ), | ||||
|                 border: OutlineInputBorder(borderRadius: _borderRadius), | ||||
|                 suffixIcon: suffixIcon, | ||||
|                 prefixIcon: prefixIcon, | ||||
|   | ||||
| @@ -5,17 +5,20 @@ class TextTitle extends StatelessWidget { | ||||
|   final String title; | ||||
|   final Color? color; | ||||
|   final double? fontSize; | ||||
|   final TextAlign? textAlign; | ||||
|   const TextTitle({ | ||||
|     Key? key, | ||||
|     required this.title, | ||||
|     this.color, | ||||
|     this.fontSize, | ||||
|     this.textAlign, | ||||
|   }) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Text( | ||||
|       title, | ||||
|       textAlign: textAlign, | ||||
|       style: TextStyle( | ||||
|         fontSize: fontSize ?? 20, | ||||
|         fontWeight: FontWeight.w700, | ||||
|   | ||||
| @@ -75,6 +75,7 @@ class ColorPalette { | ||||
|   static const Color backgroundBlueLight = Color(0xFFEBF3FD); | ||||
|   static const Color blue50 = Color(0xFFEFF6FF); | ||||
|   static const Color blue200 = Color(0xFFBFDBFE); | ||||
|   static const Color blue600 = Color(0xFF2563EB); | ||||
|   static const Color blue900 = Color(0xFF1E3A8A); | ||||
|   static const Color slate50 = Color(0xFFF8FAFC); | ||||
|   static const Color slate200 = Color(0xFFE2E8F0); | ||||
| @@ -90,8 +91,11 @@ class ColorPalette { | ||||
|   static const Color cyan100 = Color(0xFFCFFAFE); | ||||
|   static const Color cyan500 = Color(0xFF06B6D4); | ||||
|   static const Color green100 = Color(0xFFDCFCE7); | ||||
|   static const Color green300 = Color(0xFF86EFAC); | ||||
|   static const Color green400 = Color(0xFF4ADE80); | ||||
|   static const Color green500 = Color(0xFF16A34A); | ||||
|   static const Color red600 = Color(0xFFDC2626); | ||||
|   static const Color red50 = Color(0xFFFEF2F2); | ||||
|  | ||||
|   static const Map<String, Color> investTypeColor = { | ||||
|     'Money Market': purple500, | ||||
| @@ -106,4 +110,16 @@ class ColorPalette { | ||||
|     'Sharia': green100, | ||||
|     'Bonds': cyan100 | ||||
|   }; | ||||
|  | ||||
|   static const Map<String, Color> riskColor = { | ||||
|     'Moderate': orange500, | ||||
|     'Conservative': green500, | ||||
|     'Aggressive': cyan500 | ||||
|   }; | ||||
|  | ||||
|   static const Map<String, Color> textRiskColor = { | ||||
|     'Moderate': orange500, | ||||
|     'Conservative': green300, | ||||
|     'Aggressive': cyan500 | ||||
|   }; | ||||
| } | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import 'package:intl/intl.dart'; | ||||
|  | ||||
| class StringUtils { | ||||
|   static bool emailValidation(String email) { | ||||
|     return RegExp( | ||||
| @@ -13,4 +15,12 @@ class StringUtils { | ||||
|     return RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*?[\W_])(?=.{8,})') | ||||
|         .hasMatch(password); | ||||
|   } | ||||
|  | ||||
|   static String formatTime(DateTime? dateTime) { | ||||
|     if (dateTime != null) { | ||||
|       DateFormat formatter = DateFormat('HH:mm:ss'); | ||||
|       return formatter.format(dateTime); | ||||
|     } | ||||
|     return '--:--:--'; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,19 +1,12 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/back_button_view.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/route/route.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/auth/login/view/password_view.dart'; | ||||
| import 'package:cims_apps/features/auth/login/view/phone_number_view.dart'; | ||||
| import 'package:cims_apps/features/auth/login/view_model/login_view_model.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/risk_profile/risk_profile_view.dart'; | ||||
| import 'package:cims_apps/features/bottom_navigation_view.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class LoginView extends StatefulWidget { | ||||
| @@ -90,7 +83,11 @@ class _LoginViewState extends State<LoginView> { | ||||
|       currentPage++; | ||||
|       pageController.jumpToPage(1); | ||||
|     } else { | ||||
|       routePush(context, page: BottomNavigationView()); | ||||
|       routePush( | ||||
|         context, | ||||
|         page: const BottomNavigationView(), | ||||
|         routeType: RouteType.pushReplace, | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -10,70 +10,83 @@ import 'package:provider/provider.dart'; | ||||
| class PasswordView extends StatelessWidget { | ||||
|   final void Function() nextStep; | ||||
|   final TextEditingController controller; | ||||
|   const PasswordView({super.key, required this.nextStep, required this.controller}); | ||||
|   const PasswordView( | ||||
|       {super.key, required this.nextStep, required this.controller}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Consumer<LoginViewModel>( | ||||
|       builder: (context, provider, child) { | ||||
|         return Container( | ||||
|           width: SizeConfig.width, | ||||
|           height: SizeConfig.height, | ||||
|           padding: const EdgeInsets.all(24), | ||||
|           child: Column( | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               const TextTitle(title: 'Enter your password', fontSize: 24), | ||||
|               SizedBox( | ||||
|                 height: 24, | ||||
|               ), | ||||
|               TextFormView( | ||||
|                 name: 'Password', | ||||
|                 ctrl: controller, | ||||
|                 obscureText: !provider.showPassword, | ||||
|                 contentPadding: EdgeInsets.all(12), | ||||
|                 suffixIcon: GestureDetector( | ||||
|                   onTap: () { | ||||
|                     provider.changeShowPassword(); | ||||
|     return Consumer<LoginViewModel>(builder: (context, provider, child) { | ||||
|       return Container( | ||||
|         width: SizeConfig.width, | ||||
|         height: SizeConfig.height, | ||||
|         padding: const EdgeInsets.all(24), | ||||
|         child: Form( | ||||
|           key: provider.formKey, | ||||
|           child: SingleChildScrollView( | ||||
|             child: Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 const TextTitle(title: 'Enter your password', fontSize: 24), | ||||
|                 SizedBox( | ||||
|                   height: 24, | ||||
|                 ), | ||||
|                 TextFormView( | ||||
|                   name: 'Password', | ||||
|                   ctrl: controller, | ||||
|                   obscureText: !provider.showPassword, | ||||
|                   contentPadding: EdgeInsets.all(12), | ||||
|                   validator: (value) { | ||||
|                     if (value!.isEmpty) { | ||||
|                       return 'Password must filled'; | ||||
|                     } else if (value.length < 8) { | ||||
|                       return 'Minimum password 8 Character'; | ||||
|                     } else { | ||||
|                       return null; | ||||
|                     } | ||||
|                   }, | ||||
|                   child: Icon( | ||||
|                     provider.showPassword | ||||
|                         ? Icons.visibility_outlined | ||||
|                         : Icons.visibility_off_outlined, | ||||
|                     color: ColorPalette.greyDarker, | ||||
|                   suffixIcon: GestureDetector( | ||||
|                     onTap: () { | ||||
|                       provider.changeShowPassword(); | ||||
|                     }, | ||||
|                     child: Icon( | ||||
|                       provider.showPassword | ||||
|                           ? Icons.visibility_outlined | ||||
|                           : Icons.visibility_off_outlined, | ||||
|                       color: ColorPalette.greyDarker, | ||||
|                     ), | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|               TextButton( | ||||
|                 style: TextButton.styleFrom( | ||||
|                   padding: EdgeInsets.all(0) | ||||
|                 TextButton( | ||||
|                     style: TextButton.styleFrom(padding: EdgeInsets.all(0)), | ||||
|                     onPressed: () {}, | ||||
|                     child: Text( | ||||
|                       'Forget the password?', | ||||
|                       style: TextStyle( | ||||
|                         color: ColorPalette.primary, | ||||
|                       ), | ||||
|                     )), | ||||
|                 SizedBox( | ||||
|                   height: 16, | ||||
|                 ), | ||||
|                 onPressed: () { | ||||
|  | ||||
|                 }, | ||||
|                 child: Text( | ||||
|                   'Forget the password?', | ||||
|                   style: TextStyle( | ||||
|                     color: ColorPalette.primary, | ||||
|                   ), | ||||
|                 ButtonView( | ||||
|                   name: 'Confirm', | ||||
|                   heightWrapContent: true, | ||||
|                   width: SizeConfig.width, | ||||
|                   textSize: 18, | ||||
|                   marginVertical: 0, | ||||
|                   contentPadding: | ||||
|                       EdgeInsets.symmetric(horizontal: 16, vertical: 12), | ||||
|                   onPressed: () { | ||||
|                     if (provider.formKey.currentState!.validate()) { | ||||
|                       nextStep(); | ||||
|                     } | ||||
|                   }, | ||||
|                 ) | ||||
|               ), | ||||
|               SizedBox( | ||||
|                 height: 16, | ||||
|               ), | ||||
|               ButtonView( | ||||
|                 name: 'Confirm', | ||||
|                 heightWrapContent: true, | ||||
|                 width: SizeConfig.width, | ||||
|                 textSize: 18, | ||||
|                 marginVertical: 0, | ||||
|                 contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), | ||||
|                 onPressed: nextStep, | ||||
|               ) | ||||
|             ], | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|         ); | ||||
|       } | ||||
|     ); | ||||
|         ), | ||||
|       ); | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,118 +1,135 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/numeric_pad/numeric_pad.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/route/route.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/auth/login/view_model/login_view_model.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/registration_view.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class PhoneNumberView extends StatelessWidget { | ||||
|   final void Function() nextStep; | ||||
|   final TextEditingController controller; | ||||
|   const PhoneNumberView({super.key, required this.nextStep, required this.controller}); | ||||
|   const PhoneNumberView( | ||||
|       {super.key, required this.nextStep, required this.controller}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       width: SizeConfig.width, | ||||
|       height: SizeConfig.height, | ||||
|       padding: const EdgeInsets.all(24), | ||||
|       child: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           const TextTitle(title: 'Enter your phone number', fontSize: 24), | ||||
|           SizedBox( | ||||
|             height: 24, | ||||
|           ), | ||||
|           TextFormView( | ||||
|             name: 'Phone Number', | ||||
|             keyboardType: TextInputType.number, | ||||
|             ctrl: controller, | ||||
|             inputFormatters: [ | ||||
|               FilteringTextInputFormatter.deny(RegExp(r'^0')) | ||||
|             ], | ||||
|             contentPadding: EdgeInsets.all(1), | ||||
|             prefixIcon: Container( | ||||
|                 width: SizeConfig.width * .23, | ||||
|                 padding: | ||||
|                 const EdgeInsets.symmetric(horizontal: 16.0), | ||||
|                 margin: const EdgeInsets.only(right: 16), | ||||
|                 decoration: const BoxDecoration( | ||||
|                     color: ColorPalette.grey, | ||||
|                     borderRadius: BorderRadius.only( | ||||
|                       topLeft: Radius.circular(8), | ||||
|                       bottomLeft: Radius.circular(8), | ||||
|                     )), | ||||
|                 child: const Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     ImageView( | ||||
|                       image: PathAssets.iconFlag, | ||||
|                       fit: BoxFit.contain, | ||||
|                       width: 24, | ||||
|                       height: 24, | ||||
|                     ), | ||||
|                     Text( | ||||
|                       '+62', | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         color: ColorPalette.slate800, | ||||
|                       ), | ||||
|                     ) | ||||
|                   ], | ||||
|                 )), | ||||
|             validator: (value) { | ||||
|               if (value!.isEmpty) { | ||||
|                 return 'Phone number must be filled'; | ||||
|               } else { | ||||
|                 return null; | ||||
|               } | ||||
|             }, | ||||
|           ), | ||||
|           SizedBox( | ||||
|             height: 32, | ||||
|           ), | ||||
|           ButtonView( | ||||
|             name: 'Next', | ||||
|             heightWrapContent: true, | ||||
|     return ChangeNotifierProvider( | ||||
|         create: (context) => LoginViewModel(), | ||||
|         builder: (context, child) { | ||||
|           return Container( | ||||
|             width: SizeConfig.width, | ||||
|             marginVertical: 0, | ||||
|             contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 12), | ||||
|             onPressed: nextStep, | ||||
|           ), | ||||
|           Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.center, | ||||
|             children: [ | ||||
|               Text( | ||||
|                 "Don't have an account yet?", | ||||
|                 style: TextStyle( | ||||
|                   color: ColorPalette.slate500, | ||||
|                 ), | ||||
|               ), | ||||
|               TextButton( | ||||
|                   onPressed: () { | ||||
|                     routePush(context, page: RegistrationView()); | ||||
|                   }, | ||||
|                   style: TextButton.styleFrom( | ||||
|                     padding: EdgeInsets.all(0) | ||||
|             height: SizeConfig.height, | ||||
|             padding: const EdgeInsets.all(24), | ||||
|             child: | ||||
|                 Consumer<LoginViewModel>(builder: (context, provider, child) { | ||||
|               return Form( | ||||
|                 key: provider.formKey, | ||||
|                 child: SingleChildScrollView( | ||||
|                   child: Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       const TextTitle( | ||||
|                           title: 'Enter your phone number', fontSize: 24), | ||||
|                       SizedBox( | ||||
|                         height: 24, | ||||
|                       ), | ||||
|                       TextFormView( | ||||
|                         name: 'Phone Number', | ||||
|                         keyboardType: TextInputType.number, | ||||
|                         ctrl: controller, | ||||
|                         inputFormatters: [ | ||||
|                           FilteringTextInputFormatter.deny(RegExp(r'^0')) | ||||
|                         ], | ||||
|                         contentPadding: EdgeInsets.all(1), | ||||
|                         prefixIcon: Container( | ||||
|                             width: SizeConfig.width * .23, | ||||
|                             padding: | ||||
|                                 const EdgeInsets.symmetric(horizontal: 16.0), | ||||
|                             margin: const EdgeInsets.only(right: 16, left: 1, top: 1, bottom: 1), | ||||
|                             decoration: const BoxDecoration( | ||||
|                                 color: ColorPalette.grey, | ||||
|                                 borderRadius: BorderRadius.only( | ||||
|                                   topLeft: Radius.circular(8), | ||||
|                                   bottomLeft: Radius.circular(8), | ||||
|                                 )), | ||||
|                             child: const Row( | ||||
|                               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                               children: [ | ||||
|                                 ImageView( | ||||
|                                   image: PathAssets.iconFlag, | ||||
|                                   fit: BoxFit.contain, | ||||
|                                   width: 24, | ||||
|                                   height: 24, | ||||
|                                 ), | ||||
|                                 Text( | ||||
|                                   '+62', | ||||
|                                   style: TextStyle( | ||||
|                                     fontWeight: FontWeight.w600, | ||||
|                                     color: ColorPalette.slate800, | ||||
|                                   ), | ||||
|                                 ) | ||||
|                               ], | ||||
|                             )), | ||||
|                         validator: (value) { | ||||
|                           if (value!.isEmpty) { | ||||
|                             return 'Phone number must be filled'; | ||||
|                           } else { | ||||
|                             return null; | ||||
|                           } | ||||
|                         }, | ||||
|                       ), | ||||
|                       SizedBox( | ||||
|                         height: 32, | ||||
|                       ), | ||||
|                       ButtonView( | ||||
|                         name: 'Next', | ||||
|                         heightWrapContent: true, | ||||
|                         width: SizeConfig.width, | ||||
|                         marginVertical: 0, | ||||
|                         contentPadding: const EdgeInsets.symmetric( | ||||
|                             horizontal: 16, vertical: 12), | ||||
|                         onPressed: () { | ||||
|                           if (provider.formKey.currentState!.validate()) { | ||||
|                             nextStep(); | ||||
|                           } | ||||
|                         }, | ||||
|                       ), | ||||
|                       Row( | ||||
|                         mainAxisAlignment: MainAxisAlignment.center, | ||||
|                         children: [ | ||||
|                           Text( | ||||
|                             "Don't have an account yet?", | ||||
|                             style: TextStyle( | ||||
|                               color: ColorPalette.slate500, | ||||
|                             ), | ||||
|                           ), | ||||
|                           TextButton( | ||||
|                               onPressed: () { | ||||
|                                 routePush(context, page: RegistrationView()); | ||||
|                               }, | ||||
|                               style: TextButton.styleFrom( | ||||
|                                   padding: EdgeInsets.all(0)), | ||||
|                               child: Text( | ||||
|                                 'Sign Up', | ||||
|                                 style: TextStyle( | ||||
|                                     fontWeight: FontWeight.w600, | ||||
|                                     color: ColorPalette.primary), | ||||
|                               )) | ||||
|                         ], | ||||
|                       ), | ||||
|                     ], | ||||
|                   ), | ||||
|                   child: Text( | ||||
|                     'Sign Up', | ||||
|                     style: TextStyle( | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         color: ColorPalette.primary | ||||
|                     ), | ||||
|                   ) | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|                 ), | ||||
|               ); | ||||
|             }), | ||||
|           ); | ||||
|         }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| import 'package:flutter/foundation.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class LoginViewModel extends ChangeNotifier { | ||||
|   bool showPassword = false; | ||||
|   var formKey = GlobalKey<FormState>(); | ||||
|  | ||||
|   void changeShowPassword() { | ||||
|     showPassword = !showPassword; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| } | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,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/submission_data/submission_parent.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_public/view/dashboard_public_view.dart'; | ||||
| import 'package:cims_apps/features/bottom_navigation_view.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class InitialRegistrationStep extends StatelessWidget { | ||||
| @@ -203,7 +203,7 @@ class InitialRegistrationStep extends StatelessWidget { | ||||
|                   isOutlined: true, | ||||
|                   onPressed: () { | ||||
|                     routePush(context, | ||||
|                         page: const DashboardPublicView(), | ||||
|                         page: const BottomNavigationView(), | ||||
|                         routeType: RouteType.pushReplace); | ||||
|                   }, | ||||
|                 ), | ||||
|   | ||||
| @@ -45,7 +45,7 @@ class RegistrationPasswordView extends StatelessWidget { | ||||
|                         validator: (value) { | ||||
|                           if (value!.isEmpty) { | ||||
|                             return 'Password must filled'; | ||||
|                           } else if (value.length <= 8) { | ||||
|                           } else if (value.length < 8) { | ||||
|                             return 'Minimum password 8 Character'; | ||||
|                           } else { | ||||
|                             return null; | ||||
|   | ||||
| @@ -31,10 +31,11 @@ class RegistrationView extends StatelessWidget { | ||||
|           ), | ||||
|         ), | ||||
|         builder: (BuildContext context) { | ||||
|           var flutterView = View.of(context); | ||||
|           return Padding( | ||||
|             padding: EdgeInsets.only( | ||||
|               top: MediaQueryData.fromView( | ||||
|                 WidgetsBinding.instance.window, | ||||
|                 flutterView, | ||||
|               ).padding.top, | ||||
|             ), | ||||
|             child: const OtpView( | ||||
|   | ||||
| @@ -6,107 +6,120 @@ 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/submission_data/submission_parent.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class ModelDataBank { | ||||
|   final String? title, subtitle; | ||||
|   ModelDataBank(this.title, this.subtitle); | ||||
| } | ||||
|  | ||||
| class ConfirmBankAccount extends StatelessWidget { | ||||
|   const ConfirmBankAccount({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     List listData = [ | ||||
|       {'title': 'Bank Name', 'subtitle': 'Bank Mandiri'}, | ||||
|       {'title': 'Account Number', 'subtitle': '123002212084'}, | ||||
|       {'title': 'Account Owner Name', 'subtitle': 'Muhamad Rosyidin'}, | ||||
|       {'title': 'Name on ID card', 'subtitle': 'Muhamad Rosyidin'}, | ||||
|     ]; | ||||
|     return Scaffold( | ||||
|       appBar: AppBar( | ||||
|         toolbarHeight: 70, | ||||
|         backgroundColor: Colors.white, | ||||
|         surfaceTintColor: Colors.white, | ||||
|         automaticallyImplyLeading: false, | ||||
|         title: Row( | ||||
|           mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|           children: [ | ||||
|             const BackButtonView(), | ||||
|             const Text('Registration'), | ||||
|             SizedBox( | ||||
|               width: SizeConfig.width * 0.1, | ||||
|             ) | ||||
|           ], | ||||
|         ), | ||||
|         shape: const RoundedRectangleBorder( | ||||
|             side: BorderSide(color: ColorPalette.slate200)), | ||||
|       ), | ||||
|       body: SingleChildScrollView( | ||||
|         padding: const EdgeInsets.all(16.0), | ||||
|         child: SizedBox( | ||||
|           height: SizeConfig.height * .85, | ||||
|           child: Column( | ||||
|             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               const TextCaption(title: 'Bank account confirmation'), | ||||
|               SizedBox( | ||||
|                 height: SizeConfig.height * .6, | ||||
|                 child: Column( | ||||
|                   mainAxisAlignment: MainAxisAlignment.start, | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     ...listData.map((e) { | ||||
|                       return Padding( | ||||
|                         padding: const EdgeInsets.only(bottom: 16.0), | ||||
|     final listDataBank = | ||||
|         ModalRoute.of(context)!.settings.arguments as List<ModelDataBank>; | ||||
|  | ||||
|     return ChangeNotifierProvider( | ||||
|         create: (context) => SubmissionDataViewModel(), | ||||
|         builder: (context, child) { | ||||
|           return Scaffold( | ||||
|             appBar: AppBar( | ||||
|               toolbarHeight: 70, | ||||
|               backgroundColor: Colors.white, | ||||
|               surfaceTintColor: Colors.white, | ||||
|               automaticallyImplyLeading: false, | ||||
|               title: Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   const BackButtonView(), | ||||
|                   const Text('Registration'), | ||||
|                   SizedBox( | ||||
|                     width: SizeConfig.width * 0.1, | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|               shape: const RoundedRectangleBorder( | ||||
|                   side: BorderSide(color: ColorPalette.slate200)), | ||||
|             ), | ||||
|             body: SingleChildScrollView( | ||||
|               padding: const EdgeInsets.all(16.0), | ||||
|               child: SizedBox( | ||||
|                 height: SizeConfig.height * .85, | ||||
|                 child: Consumer<SubmissionDataViewModel>( | ||||
|                     builder: (context, provider, child) { | ||||
|                   return Column( | ||||
|                     mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: [ | ||||
|                       const TextCaption(title: 'Bank account confirmation'), | ||||
|                       SizedBox( | ||||
|                         height: SizeConfig.height * .6, | ||||
|                         child: Column( | ||||
|                           mainAxisAlignment: MainAxisAlignment.start, | ||||
|                           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                           children: [ | ||||
|                             Text( | ||||
|                               e['title'], | ||||
|                               style: const TextStyle( | ||||
|                                   color: ColorPalette.slate400, fontSize: 16), | ||||
|                             ), | ||||
|                             Text( | ||||
|                               e['subtitle'], | ||||
|                               style: const TextStyle( | ||||
|                                   fontSize: 16, | ||||
|                                   color: ColorPalette.slate800, | ||||
|                                   fontWeight: FontWeight.w600), | ||||
|                             ...listDataBank.map((e) { | ||||
|                               return Padding( | ||||
|                                 padding: const EdgeInsets.only(bottom: 16.0), | ||||
|                                 child: Column( | ||||
|                                   mainAxisAlignment: MainAxisAlignment.start, | ||||
|                                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                                   children: [ | ||||
|                                     Text( | ||||
|                                       e.title!, | ||||
|                                       style: const TextStyle( | ||||
|                                           color: ColorPalette.slate400, | ||||
|                                           fontSize: 16), | ||||
|                                     ), | ||||
|                                     Text( | ||||
|                                       e.subtitle!, | ||||
|                                       style: const TextStyle( | ||||
|                                           fontSize: 16, | ||||
|                                           color: ColorPalette.slate800, | ||||
|                                           fontWeight: FontWeight.w600), | ||||
|                                     ), | ||||
|                                   ], | ||||
|                                 ), | ||||
|                               ); | ||||
|                             }).toList(), | ||||
|                             const ListTileView( | ||||
|                               title: | ||||
|                                   'Make sure your data is correct as it will affect the disbursement process', | ||||
|                             ), | ||||
|                           ], | ||||
|                         ), | ||||
|                       ); | ||||
|                     }).toList(), | ||||
|                     const ListTileView( | ||||
|                       title: | ||||
|                           'Make sure your data is correct as it will affect the disbursement process', | ||||
|                     ), | ||||
|                   ], | ||||
|                 ), | ||||
|                       ), | ||||
|                       Row( | ||||
|                         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                         children: [ | ||||
|                           ButtonView( | ||||
|                             name: 'Recheck', | ||||
|                             isOutlined: true, | ||||
|                             width: SizeConfig.width * .42, | ||||
|                             onPressed: () { | ||||
|                               Navigator.pop(context); | ||||
|                             }, | ||||
|                           ), | ||||
|                           ButtonView( | ||||
|                             name: 'Confirm', | ||||
|                             width: SizeConfig.width * .42, | ||||
|                             onPressed: () { | ||||
|                               routePush(context, | ||||
|                                   page: const SubmissionParent()); | ||||
|                             }, | ||||
|                           ), | ||||
|                         ], | ||||
|                       ) | ||||
|                     ], | ||||
|                   ); | ||||
|                 }), | ||||
|               ), | ||||
|               Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   ButtonView( | ||||
|                     name: 'Recheck', | ||||
|                     isOutlined: true, | ||||
|                     width: SizeConfig.width * .42, | ||||
|                     onPressed: () { | ||||
|                       Navigator.pop(context); | ||||
|                     }, | ||||
|                   ), | ||||
|                   ButtonView( | ||||
|                     name: 'Confirm', | ||||
|                     width: SizeConfig.width * .42, | ||||
|                     onPressed: () { | ||||
|                       routePush(context, page: const SubmissionParent()); | ||||
|                     }, | ||||
|                   ), | ||||
|                 ], | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|             ), | ||||
|           ); | ||||
|         }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,108 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/text_form/text_form_view.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class ListBankView extends StatelessWidget { | ||||
|   final ValueChanged<String> onSelect; | ||||
|   const ListBankView({Key? key, required this.onSelect}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     var textTheme = Theme.of(context).textTheme; | ||||
|  | ||||
|     return ChangeNotifierProvider( | ||||
|         create: (context) => SubmissionDataViewModel(), | ||||
|         builder: (context, child) { | ||||
|           return Scaffold( | ||||
|             appBar: CustomAppBar( | ||||
|                 height: SizeConfig.height * .1, title: 'Select Bank'), | ||||
|             body: Container( | ||||
|               padding: const EdgeInsets.symmetric(horizontal: 16.0), | ||||
|               child: Consumer<SubmissionDataViewModel>( | ||||
|                   builder: (context, provider, child) { | ||||
|                 return Column( | ||||
|                   children: [ | ||||
|                     Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         ImageView( | ||||
|                           image: PathAssets.imgGuideBank, | ||||
|                           width: SizeConfig.width * .35, | ||||
|                         ), | ||||
|                         Expanded( | ||||
|                           child: Text( | ||||
|                             'Make sure the bank you choose is a bank account in your own name ', | ||||
|                             style: textTheme.bodyLarge, | ||||
|                           ), | ||||
|                         ) | ||||
|                       ], | ||||
|                     ), | ||||
|                     TextFormView( | ||||
|                       name: '', | ||||
|                       hintText: 'Search bank', | ||||
|                       ctrl: provider.ctrlBankNameSearch, | ||||
|                       contentPadding: const EdgeInsets.symmetric(vertical: 0), | ||||
|                       prefixIcon: const Icon( | ||||
|                         Icons.search, | ||||
|                         color: ColorPalette.slate500, | ||||
|                       ), | ||||
|                     ), | ||||
|                     Expanded( | ||||
|                       child: SingleChildScrollView( | ||||
|                         child: Column( | ||||
|                           children: provider.listBank.asMap().entries.map((e) { | ||||
|                             bool selectedBank = | ||||
|                                 e.key.toString() == provider.idx; | ||||
|                             return Card( | ||||
|                               elevation: 0, | ||||
|                               color: selectedBank | ||||
|                                   ? ColorPalette.blue50 | ||||
|                                   : Colors.white, | ||||
|                               shape: const RoundedRectangleBorder( | ||||
|                                 borderRadius: | ||||
|                                     BorderRadius.all(Radius.circular(12)), | ||||
|                               ), | ||||
|                               child: ListTile( | ||||
|                                 title: Text( | ||||
|                                   e.value.text, | ||||
|                                   style: TextStyle( | ||||
|                                     fontSize: 16, | ||||
|                                     fontWeight: FontWeight.w600, | ||||
|                                     color: selectedBank | ||||
|                                         ? ColorPalette.primary | ||||
|                                         : ColorPalette.slate500, | ||||
|                                   ), | ||||
|                                 ), | ||||
|                                 onTap: () { | ||||
|                                   provider.changeBank(e.key.toString()); | ||||
|                                   provider.selectBank(e.value.text); | ||||
|                                 }, | ||||
|                               ), | ||||
|                             ); | ||||
|                           }).toList(), | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                     ButtonView( | ||||
|                       name: 'Select', | ||||
|                       marginVertical: 8.0, | ||||
|                       onPressed: () { | ||||
|                         onSelect(provider.valueBank); | ||||
|                         Navigator.pop(context); | ||||
|                       }, | ||||
|                     ), | ||||
|                   ], | ||||
|                 ); | ||||
|               }), | ||||
|             ), | ||||
|           ); | ||||
|         }); | ||||
|   } | ||||
| } | ||||
| @@ -1,13 +1,13 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/select_form/select_form_view.dart'; | ||||
| import 'package:cims_apps/application/component/text_caption/text_caption.dart'; | ||||
| import 'package:cims_apps/application/component/text_form/text_form_view.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/route/route.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/data_bank/guide_screen.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/data_bank/list_bank_view.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| @@ -19,6 +19,40 @@ class SubmitBankAccount extends StatelessWidget { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     showSearchBank(TextEditingController valueCtrl) { | ||||
|       showModalBottomSheet( | ||||
|         context: context, | ||||
|         isScrollControlled: true, | ||||
|         enableDrag: false, | ||||
|         shape: const RoundedRectangleBorder( | ||||
|           borderRadius: BorderRadius.vertical( | ||||
|             top: Radius.zero, | ||||
|           ), | ||||
|         ), | ||||
|         builder: (BuildContext context) { | ||||
|           return StatefulBuilder( | ||||
|             builder: (context, setState) { | ||||
|               var flutterView = View.of(context); | ||||
|               return Padding( | ||||
|                 padding: EdgeInsets.only( | ||||
|                   top: MediaQueryData.fromView( | ||||
|                     flutterView, | ||||
|                   ).padding.top, | ||||
|                 ), | ||||
|                 child: ListBankView( | ||||
|                   onSelect: (value) { | ||||
|                     setState(() { | ||||
|                       valueCtrl.text = value; | ||||
|                     }); | ||||
|                   }, | ||||
|                 ), | ||||
|               ); | ||||
|             }, | ||||
|           ); | ||||
|         }, | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return MultiProvider( | ||||
|         providers: [ | ||||
|           ChangeNotifierProvider( | ||||
| @@ -29,52 +63,99 @@ class SubmitBankAccount extends StatelessWidget { | ||||
|           return SizedBox( | ||||
|             child: Consumer<SubmissionDataViewModel>( | ||||
|                 builder: (context, provider, child) { | ||||
|               return Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   const TextCaption(title: 'Input your bank account data'), | ||||
|                   SelectFormView( | ||||
|                     name: 'Bank Name', | ||||
|                     hintText: 'Select Bank', | ||||
|                     listItem: provider.listBank, | ||||
|                     ctrl: provider.ctrlBankName, | ||||
|                     onSelect: (value) {}, | ||||
|                   ), | ||||
|                   TextFormView( | ||||
|                     name: 'Account Number', | ||||
|                     hintText: 'Input Account Number', | ||||
|                     trailingTitleWidget: SizedBox( | ||||
|                       width: 24, | ||||
|                       child: GestureDetector( | ||||
|               return SizedBox( | ||||
|                 height: SizeConfig.height * .8, | ||||
|                 child: Form( | ||||
|                   key: provider.formKeySubmitDataBank, | ||||
|                   child: Column( | ||||
|                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                     children: [ | ||||
|                       const TextCaption(title: 'Input your bank account data'), | ||||
|                       TextFormView( | ||||
|                         name: 'Bank Name', | ||||
|                         hintText: 'Select bank', | ||||
|                         readOnly: true, | ||||
|                         ctrl: provider.ctrlBankName, | ||||
|                         onTap: () { | ||||
|                           routePush(context, page: const GuideScreen()); | ||||
|                           showSearchBank(provider.ctrlBankName); | ||||
|                         }, | ||||
|                         suffixIcon: const Icon( | ||||
|                           Icons.keyboard_arrow_down_outlined, | ||||
|                         ), | ||||
|                         validator: (value) { | ||||
|                           if (value!.isEmpty) { | ||||
|                             return 'Field must be filled'; | ||||
|                           } | ||||
|                           return null; | ||||
|                         }, | ||||
|                         child: const ImageView(image: PathAssets.iconQuestion), | ||||
|                       ), | ||||
|                     ), | ||||
|                       TextFormView( | ||||
|                         name: 'Account Number', | ||||
|                         hintText: 'Input Account Number', | ||||
|                         ctrl: provider.ctrlNoAccountBank, | ||||
|                         keyboardType: TextInputType.number, | ||||
|                         validator: (value) { | ||||
|                           if (value!.isEmpty) { | ||||
|                             return 'Field must be filled'; | ||||
|                           } | ||||
|                           return null; | ||||
|                         }, | ||||
|                         trailingTitleWidget: SizedBox( | ||||
|                           width: 24, | ||||
|                           child: GestureDetector( | ||||
|                             onTap: () { | ||||
|                               routePush(context, page: const GuideScreen()); | ||||
|                             }, | ||||
|                             child: | ||||
|                                 const ImageView(image: PathAssets.iconQuestion), | ||||
|                           ), | ||||
|                         ), | ||||
|                       ), | ||||
|                       TextFormView( | ||||
|                         name: 'Account Owner Name', | ||||
|                         hintText: 'Input Account Name', | ||||
|                         ctrl: provider.ctrlNameAccountBank, | ||||
|                         validator: (value) { | ||||
|                           if (value!.isEmpty) { | ||||
|                             return 'Field must be filled'; | ||||
|                           } | ||||
|                           return null; | ||||
|                         }, | ||||
|                       ), | ||||
|                       const Text( | ||||
|                         "Make sure the account you use is in your name, not someone else's", | ||||
|                         style: TextStyle( | ||||
|                           color: ColorPalette.slate400, | ||||
|                         ), | ||||
|                       ), | ||||
|                       SizedBox(height: SizeConfig.height * .08), | ||||
|                       ButtonView( | ||||
|                         name: 'Next', | ||||
|                         onPressed: () { | ||||
|                           if (provider.formKeySubmitDataBank.currentState! | ||||
|                               .validate()) { | ||||
|                             provider | ||||
|                                 .submitDataBank( | ||||
|                               bankName: provider.ctrlBankName.text, | ||||
|                               accountNumber: provider.ctrlNoAccountBank.text, | ||||
|                               accountName: provider.ctrlNameAccountBank.text, | ||||
|                             ) | ||||
|                                 .then((values) { | ||||
|                               provider.next(context).then((value) { | ||||
|                                 if (value) { | ||||
|                                   routePush(context, | ||||
|                                       page: const ConfirmBankAccount(), | ||||
|                                       arguments: values); | ||||
|                                 } | ||||
|                               }); | ||||
|                             }); | ||||
|                           } | ||||
|                         }, | ||||
|                       ) | ||||
|                     ], | ||||
|                   ), | ||||
|                   TextFormView( | ||||
|                     name: 'Account Owner Name', | ||||
|                     hintText: 'Input Account Name', | ||||
|                   ), | ||||
|                   const Text( | ||||
|                     "Make sure the account you use is in your name, not someone else's", | ||||
|                     style: TextStyle( | ||||
|                       color: ColorPalette.slate400, | ||||
|                     ), | ||||
|                   ), | ||||
|                   SizedBox(height: SizeConfig.height * .08), | ||||
|                   ButtonView( | ||||
|                     name: 'Next', | ||||
|                     onPressed: () { | ||||
|                       provider.next(context).then((value) { | ||||
|                         if (value) { | ||||
|                           routePush(context, page: const ConfirmBankAccount()); | ||||
|                         } | ||||
|                       }); | ||||
|                     }, | ||||
|                   ) | ||||
|                 ], | ||||
|                 ), | ||||
|               ); | ||||
|             }), | ||||
|           ); | ||||
|   | ||||
| @@ -56,17 +56,24 @@ class _QuestionViewState extends State<QuestionView> { | ||||
|           bottomNavigationBar: SizedBox( | ||||
|             height: 84, | ||||
|             child: ButtonView( | ||||
|               disabled: provider.listScore[currentPage] == 0, | ||||
|               name: 'Next', | ||||
|               marginVertical: 16, | ||||
|               disabledBgColor: ColorPalette.slate200, | ||||
|               textColor: provider.listScore[currentPage] == 0 ? ColorPalette.slate500 : Colors.white, | ||||
|               backgroundColor: ColorPalette.primary, | ||||
|               onPressed: () { | ||||
|                 if (currentPage > 3) { | ||||
|                   int totalScore = provider.listScore | ||||
|                       .reduce((value, element) => value + element); | ||||
|                   provider.setTypeResult(totalScore); | ||||
|                   routePush(context, | ||||
|                       page: ResultsView( | ||||
|                           totalScore: totalScore.toString(), | ||||
|                           typeResult: provider.typeResult)); | ||||
|                     page: ResultsView( | ||||
|                         totalScore: totalScore, | ||||
|                         typeResult: provider.typeResult | ||||
|                     ), | ||||
|                     routeType: RouteType.pushReplace | ||||
|                   ); | ||||
|                 } else { | ||||
|                   setState(() { | ||||
|                     currentPage += 1; | ||||
|   | ||||
| @@ -1,14 +1,17 @@ | ||||
| import 'package:cims_apps/application/component/button/back_button_view.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/risk_profile.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/auth/registration/view/submission_data/risk_profile/question_view.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/risk_profile/risk_profile_view.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/risk_profile/risk_profile_view_model/risk_profile_view_model.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/terms_and_condition/terms_and_condition_view.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class ResultsView extends StatelessWidget { | ||||
|   final String totalScore; | ||||
|   final int totalScore; | ||||
|   final RiskProfileResult typeResult; | ||||
|   const ResultsView( | ||||
|       {super.key, required this.typeResult, required this.totalScore}); | ||||
| @@ -37,115 +40,15 @@ class ResultsView extends StatelessWidget { | ||||
|         child: Column( | ||||
|           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|           children: [ | ||||
|             ClipRRect( | ||||
|               borderRadius: BorderRadius.circular(8), | ||||
|               child: Container( | ||||
|                 decoration: BoxDecoration( | ||||
|                     color: typeResult.color, | ||||
|                     image: DecorationImage( | ||||
|                         image: AssetImage(typeResult.img), | ||||
|                         alignment: Alignment.centerRight)), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     Container( | ||||
|                       padding: const EdgeInsets.all(24), | ||||
|                       child: Column( | ||||
|                         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                         children: [ | ||||
|                           Text( | ||||
|                             typeResult.type, | ||||
|                             style: const TextStyle( | ||||
|                                 fontWeight: FontWeight.bold, | ||||
|                                 fontSize: 24, | ||||
|                                 color: ColorPalette.white), | ||||
|                           ), | ||||
|                           const SizedBox( | ||||
|                             height: 16, | ||||
|                           ), | ||||
|                           const Text( | ||||
|                             'Total Score :', | ||||
|                             style: TextStyle( | ||||
|                                 fontWeight: FontWeight.bold, | ||||
|                                 fontSize: 16, | ||||
|                                 color: ColorPalette.white), | ||||
|                           ), | ||||
|                           Text( | ||||
|                             totalScore, | ||||
|                             style: const TextStyle( | ||||
|                                 fontWeight: FontWeight.bold, | ||||
|                                 fontSize: 28, | ||||
|                                 color: ColorPalette.white), | ||||
|                           ) | ||||
|                         ], | ||||
|                       ), | ||||
|                     ), | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             const SizedBox( | ||||
|               height: 24, | ||||
|             ), | ||||
|             Text(typeResult.desc, | ||||
|                 style: const TextStyle( | ||||
|                     color: ColorPalette.slate500, fontSize: 16)), | ||||
|             const SizedBox( | ||||
|               height: 24, | ||||
|             ), | ||||
|             const Text( | ||||
|               'Suitable Product', | ||||
|               style: TextStyle( | ||||
|                   color: ColorPalette.slate800, | ||||
|                   fontWeight: FontWeight.bold, | ||||
|                   fontSize: 16), | ||||
|             ), | ||||
|             const SizedBox( | ||||
|               height: 16, | ||||
|             ), | ||||
|             Wrap( | ||||
|               runSpacing: 16, | ||||
|               children: typeResult.suitableProduct.map((e) { | ||||
|                 return Container( | ||||
|                   padding: const EdgeInsets.all(16), | ||||
|                   decoration: BoxDecoration( | ||||
|                     borderRadius: BorderRadius.circular(6), | ||||
|                     border: Border.all(color: ColorPalette.slate200), | ||||
|                   ), | ||||
|                   child: Row( | ||||
|                     children: [ | ||||
|                       Container( | ||||
|                           padding: const EdgeInsets.all(8), | ||||
|                           alignment: Alignment.center, | ||||
|                           decoration: BoxDecoration( | ||||
|                               shape: BoxShape.circle, | ||||
|                               color: typeResult.color.withOpacity(0.1)), | ||||
|                           child: Image.asset(e['icon'], | ||||
|                               width: SizeConfig.width * 0.07, | ||||
|                               color: typeResult.color)), | ||||
|                       const SizedBox( | ||||
|                         width: 12, | ||||
|                       ), | ||||
|                       Expanded( | ||||
|                         child: Text( | ||||
|                           e['desc'], | ||||
|                           style: const TextStyle( | ||||
|                               fontSize: 16, | ||||
|                               fontWeight: FontWeight.bold, | ||||
|                               color: ColorPalette.slate800), | ||||
|                         ), | ||||
|                       ) | ||||
|                     ], | ||||
|                   ), | ||||
|                 ); | ||||
|               }).toList(), | ||||
|             ), | ||||
|             RiskProfile(totalScore: totalScore, rowSuitableProduct: false), | ||||
|             const SizedBox( | ||||
|               height: 32, | ||||
|             ), | ||||
|             ButtonView( | ||||
|               name: 'Re-test', | ||||
|               onPressed: () {}, | ||||
|               onPressed: () { | ||||
|                 routePush(context, page: QuestionView(), routeType: RouteType.pushReplace); | ||||
|               }, | ||||
|               marginVertical: 0, | ||||
|               backgroundColor: ColorPalette.white, | ||||
|               textColor: ColorPalette.primary, | ||||
|   | ||||
| @@ -14,12 +14,12 @@ class RiskProfileView extends StatelessWidget { | ||||
|   Widget build(BuildContext context) { | ||||
|     return Container( | ||||
|       width: SizeConfig.width, | ||||
|       height: SizeConfig.height, | ||||
|       padding: EdgeInsets.all(24), | ||||
|       height: SizeConfig.height * .8, | ||||
|       padding: const EdgeInsets.all(24), | ||||
|       child: Column( | ||||
|         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|         children: [ | ||||
|           Column( | ||||
|           const Column( | ||||
|             children: [ | ||||
|               ImageView(image: PathAssets.imgDataReport), | ||||
|               SizedBox( | ||||
| @@ -48,7 +48,7 @@ class RiskProfileView extends StatelessWidget { | ||||
|           ), | ||||
|           Column( | ||||
|             children: [ | ||||
|               Row( | ||||
|               const Row( | ||||
|                 mainAxisAlignment: MainAxisAlignment.center, | ||||
|                 children: [ | ||||
|                   ImageView( | ||||
| @@ -68,7 +68,7 @@ class RiskProfileView extends StatelessWidget { | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|               SizedBox( | ||||
|               const SizedBox( | ||||
|                 height: 24, | ||||
|               ), | ||||
|               ButtonView( | ||||
|   | ||||
| @@ -1,3 +1,4 @@ | ||||
| import 'package:cims_apps/application/component/button/back_button_view.dart'; | ||||
| import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/route/route.dart'; | ||||
| @@ -24,13 +25,13 @@ class SubmissionParent extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _SubmissionParentState extends State<SubmissionParent> { | ||||
|   Widget _stepItem({bool isCurrentStep = false, bool isDone = false}) { | ||||
|   Widget _stepItem({bool isCurrentStep = false}) { | ||||
|     return Container( | ||||
|       margin: const EdgeInsets.only(right: 0.0, left: 4.0), | ||||
|       height: 6, | ||||
|       width: SizeConfig.width * .08, | ||||
|       decoration: BoxDecoration( | ||||
|         color: isCurrentStep || isDone | ||||
|         color: isCurrentStep | ||||
|             ? ColorPalette.primary | ||||
|             : ColorPalette.greyBorderNeutrals, | ||||
|         borderRadius: BorderRadius.circular(50), | ||||
| @@ -66,52 +67,55 @@ class _SubmissionParentState extends State<SubmissionParent> { | ||||
|     return ChangeNotifierProvider( | ||||
|         create: (context) => SubmissionDataViewModel(), | ||||
|         builder: (context, child) { | ||||
|           return WillPopScope( | ||||
|             onWillPop: () async { | ||||
|           return PopScope( | ||||
|             canPop: false, | ||||
|             onPopInvoked: (didPop) async { | ||||
|               if (didPop) { | ||||
|                 return; | ||||
|               } | ||||
|               await routePush(context, | ||||
|                   page: const BottomNavigationView(), | ||||
|                   routeType: RouteType.pushReplace); | ||||
|               return false; | ||||
|             }, | ||||
|             child: Consumer<SubmissionDataViewModel>( | ||||
|                 builder: (context, provider, child) { | ||||
|               return Scaffold( | ||||
|                 appBar: CustomAppBar( | ||||
|                     height: SizeConfig.height * .1, title: 'Registration'), | ||||
|                 body: Stack( | ||||
|                   height: SizeConfig.height * .1, | ||||
|                   title: 'Registration', | ||||
|                   leading: BackButtonView( | ||||
|                     onPress: () { | ||||
|                       routePush( | ||||
|                         context, | ||||
|                         page: const BottomNavigationView(), | ||||
|                         routeType: RouteType.pushReplace, | ||||
|                       ); | ||||
|                     }, | ||||
|                   ), | ||||
|                 ), | ||||
|                 body: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     Column( | ||||
|                       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         Padding( | ||||
|                           padding: const EdgeInsets.symmetric( | ||||
|                               horizontal: 16.0, vertical: 16.0), | ||||
|                           child: Row( | ||||
|                             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                             children: | ||||
|                                 List.generate(provider.stepAmount, (index) { | ||||
|                               // print('indd $index'); | ||||
|                               // print( | ||||
|                               //     'getCurrentStep ${provider.getCurrentStep}'); | ||||
|                               return _stepItem( | ||||
|                                 isCurrentStep: provider.getCurrentStep == | ||||
|                                         index + 1 || | ||||
|                                     provider.getCurrentStep - 1 == index + 1, | ||||
|                                 // isDone: | ||||
|                                 //     index + 1 != provider.getCurrentStep + 1, | ||||
|                               ); | ||||
|                             }), | ||||
|                           ), | ||||
|                         ), | ||||
|                         Expanded( | ||||
|                           child: Container( | ||||
|                             padding: | ||||
|                                 const EdgeInsets.symmetric(horizontal: 16.0), | ||||
|                             child: _content(provider.getCurrentStep), | ||||
|                           ), | ||||
|                         ), | ||||
|                       ], | ||||
|                     Padding( | ||||
|                       padding: const EdgeInsets.symmetric( | ||||
|                           horizontal: 16.0, vertical: 16.0), | ||||
|                       child: Row( | ||||
|                         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                         children: List.generate(provider.stepAmount, (index) { | ||||
|                           return _stepItem( | ||||
|                             isCurrentStep: | ||||
|                                 provider.getCurrentStep == index + 1 || | ||||
|                                     provider.getCurrentStep - 1 > index, | ||||
|                           ); | ||||
|                         }), | ||||
|                       ), | ||||
|                     ), | ||||
|                     Expanded( | ||||
|                       child: SingleChildScrollView( | ||||
|                         padding: const EdgeInsets.symmetric(horizontal: 16.0), | ||||
|                         child: _content(provider.getCurrentStep), | ||||
|                       ), | ||||
|                     ), | ||||
|                   ], | ||||
|                 ), | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import 'dart:io'; | ||||
|  | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/date_picker/date_picker_view.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/text_caption/text_caption.dart'; | ||||
| import 'package:cims_apps/application/component/text_form/text_form_view.dart'; | ||||
| @@ -12,6 +13,7 @@ import 'package:cims_apps/features/auth/registration/view/submission_data/submis | ||||
| import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| import 'dart:math' as math; | ||||
|  | ||||
| class SubmitDataIdCard extends StatelessWidget { | ||||
|   const SubmitDataIdCard({Key? key}) : super(key: key); | ||||
| @@ -29,7 +31,7 @@ class SubmitDataIdCard extends StatelessWidget { | ||||
|                     color: Colors.white, | ||||
|                     borderRadius: | ||||
|                         BorderRadius.vertical(top: Radius.circular(18))), | ||||
|                 height: SizeConfig.height * .32, | ||||
|                 height: SizeConfig.height * .35, | ||||
|                 padding: const EdgeInsets.all(16.0), | ||||
|                 child: Column( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
| @@ -107,14 +109,21 @@ class SubmitDataIdCard extends StatelessWidget { | ||||
|                   SizedBox( | ||||
|                     height: SizeConfig.height * .18, | ||||
|                     width: SizeConfig.width * .45, | ||||
|                     child: ClipRRect( | ||||
|                       borderRadius: BorderRadius.circular(8), | ||||
|                       child: Image.file( | ||||
|                         File(e.image), | ||||
|                         fit: BoxFit.fill, | ||||
|                         errorBuilder: (context, error, stackTrace) { | ||||
|                           return const Icon(Icons.image_not_supported_outlined); | ||||
|                         }, | ||||
|                     child: Transform( | ||||
|                       alignment: Alignment.center, | ||||
|                       transform: e.key == 'selfie' | ||||
|                           ? Matrix4.rotationY(math.pi) | ||||
|                           : Matrix4.rotationY(0), | ||||
|                       child: ClipRRect( | ||||
|                         borderRadius: BorderRadius.circular(8), | ||||
|                         child: Image.file( | ||||
|                           File(e.image), | ||||
|                           fit: BoxFit.fill, | ||||
|                           errorBuilder: (context, error, stackTrace) { | ||||
|                             return const Icon( | ||||
|                                 Icons.image_not_supported_outlined); | ||||
|                           }, | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
| @@ -167,77 +176,107 @@ class SubmitDataIdCard extends StatelessWidget { | ||||
|           return SingleChildScrollView( | ||||
|             child: Consumer<SubmissionDataViewModel>( | ||||
|                 builder: (context, provider, child) { | ||||
|               return Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   const TextCaption( | ||||
|                       title: 'Check your ID card data for accuracy'), | ||||
|                   TextFormView(name: 'NIK'), | ||||
|                   TextFormView(name: 'Full Name'), | ||||
|                   TextFormView( | ||||
|                     name: 'Birth Date', | ||||
|                     suffixIcon: const Icon( | ||||
|                       Icons.calendar_today_rounded, | ||||
|                       color: ColorPalette.slate400, | ||||
|               return Form( | ||||
|                 key: provider.formKeySubmitIdCard, | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     const TextCaption( | ||||
|                         title: 'Check your ID card data for accuracy'), | ||||
|                     TextFormView( | ||||
|                       name: 'NIK', | ||||
|                       keyboardType: TextInputType.number, | ||||
|                       validator: (value) { | ||||
|                         if (value!.isEmpty) { | ||||
|                           return 'Field must be filled'; | ||||
|                         } | ||||
|                         return null; | ||||
|                       }, | ||||
|                     ), | ||||
|                   ), | ||||
|                   photoDocument(provider), | ||||
|                   Container( | ||||
|                     width: SizeConfig.width, | ||||
|                     padding: const EdgeInsets.symmetric(horizontal: 16.0), | ||||
|                     margin: const EdgeInsets.symmetric(vertical: 16.0), | ||||
|                     decoration: BoxDecoration( | ||||
|                       color: ColorPalette.blue50, | ||||
|                       borderRadius: BorderRadius.circular(10), | ||||
|                       border: Border.all( | ||||
|                         color: ColorPalette.greyLights, | ||||
|                         width: 1, | ||||
|                     TextFormView( | ||||
|                       name: 'Full Name', | ||||
|                       validator: (value) { | ||||
|                         if (value!.isEmpty) { | ||||
|                           return 'Field must be filled'; | ||||
|                         } | ||||
|                         return null; | ||||
|                       }, | ||||
|                     ), | ||||
|                     DatePickerView( | ||||
|                       name: 'Birth Date', | ||||
|                       ctrl: provider.ctrlBirthDate, | ||||
|                       maxDate: DateTime.now(), | ||||
|                       isMultipleSelection: false, | ||||
|                       enabled: true, | ||||
|                       validatorDate: (value) { | ||||
|                         if (value!.isEmpty) { | ||||
|                           return 'Field must be filled'; | ||||
|                         } | ||||
|                         return null; | ||||
|                       }, | ||||
|                     ), | ||||
|                     photoDocument(provider), | ||||
|                     Container( | ||||
|                       width: SizeConfig.width, | ||||
|                       padding: const EdgeInsets.symmetric(horizontal: 16.0), | ||||
|                       margin: const EdgeInsets.symmetric(vertical: 16.0), | ||||
|                       decoration: BoxDecoration( | ||||
|                         color: ColorPalette.blue50, | ||||
|                         borderRadius: BorderRadius.circular(10), | ||||
|                         border: Border.all( | ||||
|                           color: ColorPalette.greyLights, | ||||
|                           width: 1, | ||||
|                         ), | ||||
|                       ), | ||||
|                     ), | ||||
|                     child: Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         const ImageView( | ||||
|                           image: PathAssets.iconShield, | ||||
|                           width: 20, | ||||
|                           height: 22, | ||||
|                         ), | ||||
|                         const SizedBox( | ||||
|                           width: 8, | ||||
|                         ), | ||||
|                         const Expanded( | ||||
|                           child: Text( | ||||
|                             'Will my data be safe?', | ||||
|                             style: TextStyle( | ||||
|                               fontWeight: FontWeight.w600, | ||||
|                               color: ColorPalette.primary, | ||||
|                       child: Row( | ||||
|                         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                         children: [ | ||||
|                           const ImageView( | ||||
|                             image: PathAssets.iconShield, | ||||
|                             width: 20, | ||||
|                             height: 22, | ||||
|                           ), | ||||
|                           const SizedBox( | ||||
|                             width: 8, | ||||
|                           ), | ||||
|                           const Expanded( | ||||
|                             child: Text( | ||||
|                               'Will my data be safe?', | ||||
|                               style: TextStyle( | ||||
|                                 fontWeight: FontWeight.w600, | ||||
|                                 color: ColorPalette.primary, | ||||
|                               ), | ||||
|                             ), | ||||
|                           ), | ||||
|                         ), | ||||
|                         IconButton( | ||||
|                           onPressed: () { | ||||
|                             bottomSheet(); | ||||
|                           }, | ||||
|                           icon: const Icon( | ||||
|                             Icons.arrow_forward_ios, | ||||
|                             color: ColorPalette.primary, | ||||
|                             size: 20, | ||||
|                           ), | ||||
|                         ) | ||||
|                       ], | ||||
|                           IconButton( | ||||
|                             onPressed: () { | ||||
|                               bottomSheet(); | ||||
|                             }, | ||||
|                             icon: const Icon( | ||||
|                               Icons.arrow_forward_ios, | ||||
|                               color: ColorPalette.primary, | ||||
|                               size: 20, | ||||
|                             ), | ||||
|                           ) | ||||
|                         ], | ||||
|                       ), | ||||
|                     ), | ||||
|                   ), | ||||
|                   ButtonView( | ||||
|                     name: 'Next', | ||||
|                     onPressed: () async { | ||||
|                       await provider.next(context).then((value) { | ||||
|                         if (value) { | ||||
|                           routePush(context, page: const SubmissionParent()); | ||||
|                     ButtonView( | ||||
|                       name: 'Next', | ||||
|                       onPressed: () async { | ||||
|                         if (provider.formKeySubmitIdCard.currentState! | ||||
|                             .validate()) { | ||||
|                           await provider.next(context).then((value) { | ||||
|                             if (value) { | ||||
|                               routePush(context, | ||||
|                                   page: const SubmissionParent()); | ||||
|                             } | ||||
|                           }); | ||||
|                         } | ||||
|                       }); | ||||
|                     }, | ||||
|                   ) | ||||
|                 ], | ||||
|                       }, | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ); | ||||
|             }), | ||||
|           ); | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import 'package:cims_apps/application/component/text_caption/text_caption.dart'; | ||||
| import 'package:cims_apps/application/component/text_form/text_form_view.dart'; | ||||
| import 'package:cims_apps/core/route/route.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/core/utils/string_utils.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/submission_parent.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | ||||
| import 'package:flutter/gestures.dart'; | ||||
| @@ -14,44 +15,70 @@ import 'package:provider/provider.dart'; | ||||
| class SubmitEmail extends StatelessWidget { | ||||
|   const SubmitEmail({Key? key}) : super(key: key); | ||||
|  | ||||
|   Widget _emailVerify() { | ||||
|     return Column( | ||||
|       children: [ | ||||
|         const ImageView(image: PathAssets.imgEmail), | ||||
|         Align( | ||||
|           alignment: Alignment.center, | ||||
|           child: RichText( | ||||
|             textAlign: TextAlign.center, | ||||
|             text: TextSpan(children: [ | ||||
|               const TextSpan( | ||||
|                 text: | ||||
|                     'We have sent a verification link to your e-mail. \nPlease check your email for ', | ||||
|                 style: TextStyle( | ||||
|                   color: Colors.black, | ||||
|                   decoration: TextDecoration.none, | ||||
|   Widget _emailVerify(BuildContext context, SubmissionDataViewModel provider) { | ||||
|     var textTheme = Theme.of(context).textTheme; | ||||
|     return Padding( | ||||
|       padding: const EdgeInsets.all(24.0), | ||||
|       child: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           const TextCaption(title: 'Check your e-mail'), | ||||
|           const ImageView(image: PathAssets.imgEmail), | ||||
|           Align( | ||||
|             alignment: Alignment.center, | ||||
|             child: RichText( | ||||
|               textAlign: TextAlign.center, | ||||
|               text: TextSpan(children: [ | ||||
|                 TextSpan( | ||||
|                   text: | ||||
|                       'We have sent a verification link to your e-mail. \nPlease check your email for ', | ||||
|                   style: textTheme.displayMedium, | ||||
|                 ), | ||||
|               ), | ||||
|               TextSpan( | ||||
|                 recognizer: TapGestureRecognizer() | ||||
|                   ..onTap = () { | ||||
|                     print('object'); | ||||
|                   }, | ||||
|                 text: 'verification', | ||||
|                 style: const TextStyle( | ||||
|                   color: Colors.blue, | ||||
|                 TextSpan( | ||||
|                   recognizer: TapGestureRecognizer() | ||||
|                     ..onTap = () async { | ||||
|                       await provider.next(context).then((value) { | ||||
|                         if (value) { | ||||
|                           routePush(context, page: const SubmissionParent()); | ||||
|                         } | ||||
|                       }); | ||||
|                     }, | ||||
|                   text: 'verification', | ||||
|                   style: const TextStyle( | ||||
|                     color: Colors.blue, | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|               const TextSpan( | ||||
|                 text: ' to \ncontinue registration.', | ||||
|                 style: TextStyle( | ||||
|                   color: Colors.black, | ||||
|                   decoration: TextDecoration.none, | ||||
|                 TextSpan( | ||||
|                   text: ' to \ncontinue registration.', | ||||
|                   style: textTheme.displayMedium, | ||||
|                 ), | ||||
|               ), | ||||
|             ]), | ||||
|               ]), | ||||
|             ), | ||||
|           ), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   showEmailVerify(BuildContext context, SubmissionDataViewModel provider) { | ||||
|     showModalBottomSheet( | ||||
|       context: context, | ||||
|       isScrollControlled: true, | ||||
|       enableDrag: false, | ||||
|       shape: const RoundedRectangleBorder( | ||||
|         borderRadius: BorderRadius.vertical( | ||||
|           top: Radius.zero, | ||||
|         ), | ||||
|       ], | ||||
|       ), | ||||
|       builder: (context) { | ||||
|         var flutterView = View.of(context); | ||||
|         return Padding( | ||||
|           padding: EdgeInsets.only( | ||||
|             top: MediaQueryData.fromView(flutterView).padding.top, | ||||
|           ), | ||||
|           child: _emailVerify(context, provider), | ||||
|         ); | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @@ -62,35 +89,41 @@ class SubmitEmail extends StatelessWidget { | ||||
|         builder: (context, child) { | ||||
|           return Consumer<SubmissionDataViewModel>( | ||||
|               builder: (context, provider, child) { | ||||
|             return SingleChildScrollView( | ||||
|               child: Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 // mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   !provider.isEmailVerify | ||||
|                       ? const TextCaption(title: 'Enter your e-mail') | ||||
|                       : const TextCaption(title: 'Check your e-mail '), | ||||
|                   !provider.isEmailVerify | ||||
|                       ? TextFormView( | ||||
|                           name: 'E-mail Address', | ||||
|                           hintText: 'Input e-mail address', | ||||
|                           // onTap: () { | ||||
|                           //   provider.submitEmail(); | ||||
|                           // }, | ||||
|                         ) | ||||
|                       : _emailVerify(), | ||||
|                   SizedBox(height: SizeConfig.height * .42), | ||||
|                   ButtonView( | ||||
|                     name: 'Next', | ||||
|                     onPressed: () async { | ||||
|                       await provider.next(context).then((value) { | ||||
|                         if (value) { | ||||
|                           routePush(context, page: const SubmissionParent()); | ||||
|             return SizedBox( | ||||
|               height: SizeConfig.height * .78, | ||||
|               child: Form( | ||||
|                 key: provider.formKeySubmitEmail, | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceAround, | ||||
|                   children: [ | ||||
|                     const TextCaption(title: 'Enter your e-mail'), | ||||
|                     TextFormView( | ||||
|                       name: 'E-mail Address', | ||||
|                       hintText: 'Input e-mail address', | ||||
|                       keyboardType: TextInputType.emailAddress, | ||||
|                       validator: (value) { | ||||
|                         if (value!.isEmpty) { | ||||
|                           return 'Filled cannot be empty'; | ||||
|                         } else if (!StringUtils.emailValidation(value)) { | ||||
|                           return 'Format email wrong'; | ||||
|                         } else { | ||||
|                           return null; | ||||
|                         } | ||||
|                       }); | ||||
|                     }, | ||||
|                   ) | ||||
|                 ], | ||||
|                       }, | ||||
|                     ), | ||||
|                     SizedBox(height: SizeConfig.height * .43), | ||||
|                     ButtonView( | ||||
|                       name: 'Next', | ||||
|                       onPressed: () async { | ||||
|                         if (provider.formKeySubmitEmail.currentState! | ||||
|                             .validate()) { | ||||
|                           showEmailVerify(context, provider); | ||||
|                         } | ||||
|                       }, | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|             ); | ||||
|           }); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/select_form/select_form_view.dart'; | ||||
| import 'package:cims_apps/application/component/text_caption/text_caption.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/submission_data/submission_parent.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| @@ -17,12 +18,13 @@ class SubmitPersonalData extends StatelessWidget { | ||||
|         builder: (context, child) { | ||||
|           return Consumer<SubmissionDataViewModel>( | ||||
|               builder: (context, provider, child) { | ||||
|             return SingleChildScrollView( | ||||
|             return SizedBox( | ||||
|               height: SizeConfig.height * .78, | ||||
|               child: Form( | ||||
|                 key: provider.formKeyPersonalData, | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceAround, | ||||
|                   children: [ | ||||
|                     const TextCaption(title: 'Your personal details'), | ||||
|                     SelectFormView( | ||||
| @@ -31,6 +33,12 @@ class SubmitPersonalData extends StatelessWidget { | ||||
|                       ctrl: provider.ctrlOccupation, | ||||
|                       listItem: provider.listOccupation, | ||||
|                       onSelect: (value) {}, | ||||
|                       validator: (value) { | ||||
|                         if (value!.isEmpty) { | ||||
|                           return 'Field must be filled'; | ||||
|                         } | ||||
|                         return null; | ||||
|                       }, | ||||
|                     ), | ||||
|                     SelectFormView( | ||||
|                       name: 'Income Level (IDR)', | ||||
| @@ -38,6 +46,12 @@ class SubmitPersonalData extends StatelessWidget { | ||||
|                       ctrl: provider.ctrlIncome, | ||||
|                       listItem: provider.listIncome, | ||||
|                       onSelect: (value) {}, | ||||
|                       validator: (value) { | ||||
|                         if (value!.isEmpty) { | ||||
|                           return 'Field must be filled'; | ||||
|                         } | ||||
|                         return null; | ||||
|                       }, | ||||
|                     ), | ||||
|                     SelectFormView( | ||||
|                       name: 'Marital Status', | ||||
| @@ -45,6 +59,12 @@ class SubmitPersonalData extends StatelessWidget { | ||||
|                       ctrl: provider.ctrlMarital, | ||||
|                       listItem: provider.listMarital, | ||||
|                       onSelect: (value) {}, | ||||
|                       validator: (value) { | ||||
|                         if (value!.isEmpty) { | ||||
|                           return 'Field must be filled'; | ||||
|                         } | ||||
|                         return null; | ||||
|                       }, | ||||
|                     ), | ||||
|                     SelectFormView( | ||||
|                       name: 'Source of Fund', | ||||
| @@ -52,20 +72,28 @@ class SubmitPersonalData extends StatelessWidget { | ||||
|                       ctrl: provider.ctrlSourceFund, | ||||
|                       listItem: provider.listSourceFund, | ||||
|                       onSelect: (value) {}, | ||||
|                       validator: (value) { | ||||
|                         if (value!.isEmpty) { | ||||
|                           return 'Field must be filled'; | ||||
|                         } | ||||
|                         return null; | ||||
|                       }, | ||||
|                     ), | ||||
|                     Align( | ||||
|                       alignment: Alignment.bottomCenter, | ||||
|                       child: ButtonView( | ||||
|                         name: 'Next', | ||||
|                         onPressed: () async { | ||||
|                     // SizedBox(height: SizeConfig.height * .18), | ||||
|                     ButtonView( | ||||
|                       marginVertical: 0, | ||||
|                       name: 'Next', | ||||
|                       onPressed: () async { | ||||
|                         if (provider.formKeyPersonalData.currentState! | ||||
|                             .validate()) { | ||||
|                           await provider.next(context).then((value) { | ||||
|                             if (value) { | ||||
|                               routePush(context, | ||||
|                                   page: const SubmissionParent()); | ||||
|                             } | ||||
|                           }); | ||||
|                         }, | ||||
|                       ), | ||||
|                         } | ||||
|                       }, | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|   | ||||
| @@ -24,26 +24,30 @@ class InitialSignature extends StatelessWidget { | ||||
|         builder: (context, child) { | ||||
|           return Consumer<SubmissionDataViewModel>( | ||||
|               builder: (context, provider, child) { | ||||
|             return Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 const TextCaption(title: 'Draw your digital sign'), | ||||
|                 const ImageView(image: PathAssets.frameSignature), | ||||
|                 const ListTileView( | ||||
|                     title: | ||||
|                         'Make sure the sign you draw is match with your ID Card'), | ||||
|                 SizedBox(height: SizeConfig.height * .07), | ||||
|                 ButtonView( | ||||
|                   name: 'Next', | ||||
|                   onPressed: () { | ||||
|                     provider.next(context).then((value) { | ||||
|                       if (value) { | ||||
|                         routePush(context, page: const SubmitSignature()); | ||||
|                       } | ||||
|                     }); | ||||
|                   }, | ||||
|                 ) | ||||
|               ], | ||||
|             return SizedBox( | ||||
|               height: SizeConfig.height * .8, | ||||
|               child: Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                 children: [ | ||||
|                   const TextCaption(title: 'Draw your digital sign'), | ||||
|                   const ImageView(image: PathAssets.frameSignature), | ||||
|                   const ListTileView( | ||||
|                       title: | ||||
|                           'Make sure the sign you draw is match with your ID Card'), | ||||
|                   SizedBox(height: SizeConfig.height * .1), | ||||
|                   ButtonView( | ||||
|                     name: 'Next', | ||||
|                     onPressed: () { | ||||
|                       provider.next(context).then((value) { | ||||
|                         if (value) { | ||||
|                           routePush(context, page: const SubmitSignature()); | ||||
|                         } | ||||
|                       }); | ||||
|                     }, | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|             ); | ||||
|           }); | ||||
|         }); | ||||
|   | ||||
| @@ -3,6 +3,7 @@ import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/set_pin_view/set_pin_view.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/route/route.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| @@ -60,47 +61,48 @@ class TermsAndConditionView extends StatelessWidget { | ||||
|                         ], | ||||
|                       ), | ||||
|                     ); | ||||
|                   }) | ||||
|                   }), | ||||
|                   Consumer<SubmissionDataViewModel>( | ||||
|                       builder: (context, provider, child) { | ||||
|                     return Padding( | ||||
|                       padding: const EdgeInsets.only(top: 24.0), | ||||
|                       child: Row( | ||||
|                         children: [ | ||||
|                           Expanded( | ||||
|                               child: ButtonView( | ||||
|                             name: 'Decline', | ||||
|                             onPressed: () { | ||||
|                               Navigator.pop(context); | ||||
|                             }, | ||||
|                             height: SizeConfig.height * .06, | ||||
|                             marginVertical: 0, | ||||
|                             backgroundColor: ColorPalette.white, | ||||
|                             textColor: ColorPalette.primary, | ||||
|                             isOutlined: true, | ||||
|                             borderColor: ColorPalette.primary, | ||||
|                           )), | ||||
|                           const SizedBox(width: 16), | ||||
|                           Expanded( | ||||
|                               child: ButtonView( | ||||
|                                   name: 'Accept', | ||||
|                                   onPressed: () { | ||||
|                                     provider.nextSubmission(context); | ||||
|                                     routePush(context, | ||||
|                                         routeType: RouteType.pushReplace, | ||||
|                                         page: SetPinView( | ||||
|                                           currentPin: '', | ||||
|                                           submitPin: (context, pin) {}, | ||||
|                                         )); | ||||
|                                   }, | ||||
|                                   height: SizeConfig.height * .06, | ||||
|                                   marginVertical: 0)) | ||||
|                         ], | ||||
|                       ), | ||||
|                     ); | ||||
|                   }), | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|             bottomNavigationBar: Consumer<SubmissionDataViewModel>( | ||||
|                 builder: (context, provider, child) { | ||||
|               return Container( | ||||
|                 height: 84, | ||||
|                 padding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                 child: Row( | ||||
|                   children: [ | ||||
|                     Expanded( | ||||
|                         child: ButtonView( | ||||
|                       name: 'Decline', | ||||
|                       onPressed: () { | ||||
|                         Navigator.pop(context); | ||||
|                       }, | ||||
|                       marginVertical: 16, | ||||
|                       backgroundColor: ColorPalette.white, | ||||
|                       textColor: ColorPalette.primary, | ||||
|                       isOutlined: true, | ||||
|                       borderColor: ColorPalette.primary, | ||||
|                     )), | ||||
|                     const SizedBox(width: 16), | ||||
|                     Expanded( | ||||
|                         child: ButtonView( | ||||
|                             name: 'Accept', | ||||
|                             onPressed: () { | ||||
|                               provider.nextSubmission(context); | ||||
|                               routePush(context, | ||||
|                                   routeType: RouteType.pushReplace, | ||||
|                                   page: SetPinView( | ||||
|                                     currentPin: '', | ||||
|                                     submitPin: (context, pin) {}, | ||||
|                                   )); | ||||
|                             }, | ||||
|                             marginVertical: 16)) | ||||
|                   ], | ||||
|                 ), | ||||
|               ); | ||||
|             }), | ||||
|           ); | ||||
|         }); | ||||
|   } | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import 'package:camera/camera.dart'; | ||||
| import 'package:cims_apps/application/component/select_form/select_form_view.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/data_bank/confirm_bank_account.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:shared_preferences/shared_preferences.dart'; | ||||
|  | ||||
| @@ -14,12 +15,21 @@ class SubmissionDataViewModel extends ChangeNotifier { | ||||
|   bool _isEmailVerify = false; | ||||
|   bool get isEmailVerify => _isEmailVerify; | ||||
|   var formKeyPersonalData = GlobalKey<FormState>(); | ||||
|   var formKeySubmitEmail = GlobalKey<FormState>(); | ||||
|   var formKeySubmitIdCard = GlobalKey<FormState>(); | ||||
|   var formKeySubmitDataBank = GlobalKey<FormState>(); | ||||
|   TextEditingController ctrlOccupation = TextEditingController(); | ||||
|   TextEditingController ctrlIncome = TextEditingController(); | ||||
|   TextEditingController ctrlMarital = TextEditingController(); | ||||
|   TextEditingController ctrlSourceFund = TextEditingController(); | ||||
|   TextEditingController ctrlBankName = TextEditingController(); | ||||
|   TextEditingController ctrlNameAccountBank = TextEditingController(); | ||||
|   TextEditingController ctrlNoAccountBank = TextEditingController(); | ||||
|   TextEditingController ctrlBankNameSearch = TextEditingController(); | ||||
|   TextEditingController ctrlBirthDate = TextEditingController(); | ||||
|   int step = 1; | ||||
|   String? idx; | ||||
|   String valueBank = ''; | ||||
|  | ||||
|   List<ItemSelectForm> listOccupation = [ | ||||
|     ItemSelectForm('key1', 'Student'), | ||||
| @@ -43,16 +53,18 @@ class SubmissionDataViewModel extends ChangeNotifier { | ||||
|   ]; | ||||
|  | ||||
|   List<ItemSelectForm> listBank = [ | ||||
|     ItemSelectForm('key1', 'BCA'), | ||||
|     ItemSelectForm('key2', 'BRI'), | ||||
|     ItemSelectForm('key3', 'BNI'), | ||||
|     ItemSelectForm('key4', 'BANK MANDIRI'), | ||||
|     ItemSelectForm('key5', 'CIMB NIAGA'), | ||||
|     ItemSelectForm('0', 'BCA'), | ||||
|     ItemSelectForm('1', 'BRI'), | ||||
|     ItemSelectForm('2', 'BNI'), | ||||
|     ItemSelectForm('3', 'BANK MANDIRI'), | ||||
|     ItemSelectForm('4', 'CIMB NIAGA'), | ||||
|     ItemSelectForm('5', 'PERMATA'), | ||||
|     ItemSelectForm('6', 'BANK JATENG'), | ||||
|   ]; | ||||
|  | ||||
|   List<ItemSelectForm> listImg = [ | ||||
|     ItemSelectForm('', 'ID Card', image: ''), | ||||
|     ItemSelectForm('', 'Selfie with ID Card', image: ''), | ||||
|     ItemSelectForm('ktp', 'ID Card', image: ''), | ||||
|     ItemSelectForm('selfie', 'Selfie with ID Card', image: ''), | ||||
|   ]; | ||||
|  | ||||
|   Future<List<CameraDescription>> initCamera() async { | ||||
| @@ -104,4 +116,31 @@ class SubmissionDataViewModel extends ChangeNotifier { | ||||
|     } | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   changeBank(String key) { | ||||
|     idx = key; | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   selectBank(String value) { | ||||
|     valueBank = value; | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   List<ModelDataBank> listDataBank = []; | ||||
|  | ||||
|   Future<List<ModelDataBank>?> submitDataBank( | ||||
|       {required String bankName, accountNumber, accountName}) async { | ||||
|     List<ModelDataBank> listResponse = [ | ||||
|       ModelDataBank('Bank Name', bankName), | ||||
|       ModelDataBank('Account Number', accountNumber), | ||||
|       ModelDataBank('Account Owner Name', accountName), | ||||
|       ModelDataBank('Name on ID card', accountName), | ||||
|     ]; | ||||
|     if (listResponse.first.subtitle != null) { | ||||
|       // listDataBank = listResponse; | ||||
|       return listResponse; | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,18 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/homepage/homepage_view.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view/plan_view.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/portfolio_view.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/profile/view/profile_view.dart'; | ||||
| import 'package:cims_apps/features/transaction/view/transaction_view.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class BottomNavigationItem { | ||||
|   String icon, label; | ||||
|  | ||||
|   BottomNavigationItem(this.icon, this.label); | ||||
| } | ||||
|  | ||||
| class BottomNavigationView extends StatefulWidget { | ||||
|   const BottomNavigationView({Key? key}) : super(key: key); | ||||
| @@ -18,41 +26,28 @@ class _BottomNavigationViewState extends State<BottomNavigationView> { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     ///TODO: masukan pagenya dilistWidget ini | ||||
|     List<Widget> listWidget = [ | ||||
|     List<Widget> listWidget = const [ | ||||
|       HomeView(), | ||||
|       PlanView(), | ||||
|       Container(), | ||||
|       TransactionView(), | ||||
|       PortofolioView(), | ||||
|       Container(), | ||||
|       ProfileView(), | ||||
|     ]; | ||||
|  | ||||
|     List<BottomNavigationBarItem> listNavigation = const [ | ||||
|       BottomNavigationBarItem( | ||||
|         icon: Icon(Icons.home_outlined), | ||||
|         label: 'Home', | ||||
|       ), | ||||
|       BottomNavigationBarItem( | ||||
|         icon: Icon(Icons.file_open), | ||||
|         label: 'Plan', | ||||
|       ), | ||||
|       BottomNavigationBarItem( | ||||
|         icon: Icon(Icons.compare_arrows), | ||||
|         label: 'Transaction', | ||||
|       ), | ||||
|       BottomNavigationBarItem( | ||||
|         icon: Icon(Icons.pie_chart_rounded), | ||||
|         label: 'Portfolio', | ||||
|       ), | ||||
|       BottomNavigationBarItem( | ||||
|         icon: Icon(Icons.person), | ||||
|         label: 'Profile', | ||||
|       ), | ||||
|     List<BottomNavigationItem> listNavigation = [ | ||||
|       BottomNavigationItem(PathAssets.iconNavigationHome, 'Home'), | ||||
|       BottomNavigationItem(PathAssets.iconNavigationPlan, 'Plan'), | ||||
|       BottomNavigationItem(PathAssets.iconNavigationTransaction, 'Transaction'), | ||||
|       BottomNavigationItem(PathAssets.iconNavigationPortfolio, 'Portfolio'), | ||||
|       BottomNavigationItem(PathAssets.iconNavigationProfile, 'Profile') | ||||
|     ]; | ||||
|  | ||||
|     return Scaffold( | ||||
|       body: listWidget[_selectedIndex], | ||||
|       bottomNavigationBar: Padding( | ||||
|       bottomNavigationBar: Container( | ||||
|         decoration: const BoxDecoration( | ||||
|           border: Border(top: BorderSide(color: ColorPalette.slate200)) | ||||
|         ), | ||||
|         padding: const EdgeInsets.symmetric(vertical: 12), | ||||
|         child: BottomNavigationBar( | ||||
|           elevation: 0, | ||||
| @@ -62,13 +57,25 @@ class _BottomNavigationViewState extends State<BottomNavigationView> { | ||||
|             }); | ||||
|           }, | ||||
|           currentIndex: _selectedIndex, | ||||
|           items: listNavigation, | ||||
|           items: listNavigation.asMap().entries.map((e) { | ||||
|             return BottomNavigationBarItem( | ||||
|               icon: Padding( | ||||
|                 padding: const EdgeInsets.only(bottom: 4), | ||||
|                 child: Image.asset( | ||||
|                   e.value.icon, | ||||
|                   width: SizeConfig.width * 0.06, | ||||
|                   color: e.key == _selectedIndex ? ColorPalette.primary : ColorPalette.slate800 | ||||
|                 ), | ||||
|               ), | ||||
|               label: e.value.label | ||||
|             ); | ||||
|           }).toList(), | ||||
|           type: BottomNavigationBarType.fixed, | ||||
|           showUnselectedLabels: true, | ||||
|           selectedItemColor: ColorPalette.primary, | ||||
|           unselectedItemColor: Colors.black, | ||||
|           selectedLabelStyle: const TextStyle(color: ColorPalette.primary), | ||||
|           unselectedLabelStyle: const TextStyle(color: Colors.black), | ||||
|           selectedLabelStyle: const TextStyle(color: ColorPalette.primary, fontSize: 12), | ||||
|           unselectedLabelStyle: const TextStyle(color: Colors.black, fontSize: 12), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   | ||||
| @@ -7,7 +7,6 @@ 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/initial_registration_step.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/registration_view.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/invest_type/invest_type_view.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| @@ -50,94 +49,110 @@ class _HomeViewState extends State<HomeView> { | ||||
|     InvestType('Sharia', PathAssets.iconPortofolioSharia) | ||||
|   ]; | ||||
|  | ||||
|   StepVerification listStepVerification = StepVerification(0, [ | ||||
|     'Registration', 'Verification', 'Complete' | ||||
|   ]); | ||||
|   StepVerification listStepVerification = | ||||
|       StepVerification(1, ['Registration', '', 'Verification', '', 'Complete']); | ||||
|  | ||||
|   List<Article> 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), | ||||
|     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, | ||||
|       backgroundColor: ColorPalette.slate50, | ||||
|       body: CustomScrollView( | ||||
|         slivers: [ | ||||
|           SliverAppBar( | ||||
|             onStretchTrigger: () async { | ||||
|             }, | ||||
|             expandedHeight: 325, | ||||
|             collapsedHeight: 325, | ||||
|             leading: const SizedBox(), | ||||
|             surfaceTintColor: ColorPalette.slate50, | ||||
|             backgroundColor: ColorPalette.slate50, | ||||
|             flexibleSpace: FlexibleSpaceBar( | ||||
|               background: Stack( | ||||
|                 children: [ | ||||
|                   const ImageView(image: PathAssets.imgDashboardAccount), | ||||
|                   Column( | ||||
|                     children: [ | ||||
|                       const Text( | ||||
|                         'Home', | ||||
|                         style: TextStyle( | ||||
|                             color: Colors.white, | ||||
|                             fontSize: 20, | ||||
|                             fontWeight: FontWeight.w700 | ||||
|                       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)) | ||||
|                           ], | ||||
|                         ), | ||||
|                       ), | ||||
|                       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: 24, | ||||
|                       ), | ||||
|                     ], | ||||
|                   ), | ||||
|                 ), | ||||
|                 const SizedBox( | ||||
|                   height: 32, | ||||
|                 ), | ||||
|                 Padding( | ||||
|                   padding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                   child: portofolioValue(), | ||||
|                 ), | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|             floating: true, | ||||
|             snap: true, | ||||
|           ), | ||||
|           SliverToBoxAdapter( | ||||
|             child:  Column( | ||||
|               children: [ | ||||
|                 cardVerification(), | ||||
|                 const SizedBox( | ||||
|                   height: 24, | ||||
|                 ), | ||||
|                 cardInvestType(), | ||||
|                 infoAndPromo(), | ||||
|                 const SizedBox( | ||||
|                   height: 32, | ||||
|                 ), | ||||
|                 Expanded( | ||||
|                   child: ListView( | ||||
|                     padding: const EdgeInsets.all(0), | ||||
|                     children: [ | ||||
|                       cardVerification(), | ||||
|                       const SizedBox( | ||||
|                         height: 24, | ||||
|                       ), | ||||
|                       infoAndPromo(), | ||||
|                       const SizedBox( | ||||
|                         height: 24, | ||||
|                       ), | ||||
|                       articles(), | ||||
|                     ], | ||||
|                   ), | ||||
|                   height: 24, | ||||
|                 ), | ||||
|                 articles(), | ||||
|               ], | ||||
|             ) | ||||
|           ], | ||||
|         ), | ||||
|             ), | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| @@ -148,7 +163,8 @@ class _HomeViewState extends State<HomeView> { | ||||
|       children: [ | ||||
|         Row( | ||||
|           children: [ | ||||
|             const Text('Portofolio Value', style: TextStyle(color: Colors.white)), | ||||
|             const Text('Portofolio Value', | ||||
|                 style: TextStyle(color: Colors.white)), | ||||
|             const SizedBox( | ||||
|               width: 12, | ||||
|             ), | ||||
| @@ -158,8 +174,11 @@ class _HomeViewState extends State<HomeView> { | ||||
|                     seePortofolioValue = !seePortofolioValue; | ||||
|                   }); | ||||
|                 }, | ||||
|                 child: const Icon(Icons.visibility_outlined, color: Color(0xff93C5FD)) | ||||
|             ) | ||||
|                 child: Icon( | ||||
|                     seePortofolioValue | ||||
|                         ? Icons.visibility_off_outlined | ||||
|                         : Icons.visibility_outlined, | ||||
|                     color: const Color(0xff93C5FD))) | ||||
|           ], | ||||
|         ), | ||||
|         const SizedBox( | ||||
| @@ -170,24 +189,28 @@ class _HomeViewState extends State<HomeView> { | ||||
|             AnimatedCrossFade( | ||||
|               duration: const Duration(milliseconds: 300), | ||||
|               alignment: Alignment.center, | ||||
|               crossFadeState: seePortofolioValue ? CrossFadeState.showSecond : CrossFadeState.showFirst, | ||||
|               crossFadeState: seePortofolioValue | ||||
|                   ? CrossFadeState.showSecond | ||||
|                   : CrossFadeState.showFirst, | ||||
|               firstChild: RichText( | ||||
|                   text: const TextSpan( | ||||
|                       text: 'Rp ', | ||||
|                       style: TextStyle(fontSize: 32, color: Color(0xff93C5FD), fontFamily: 'Manrope',), | ||||
|                       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', | ||||
|                           ), | ||||
|                         ) | ||||
|                       ] | ||||
|                   ) | ||||
|               ), | ||||
|                     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( | ||||
| @@ -197,7 +220,8 @@ class _HomeViewState extends State<HomeView> { | ||||
|                       Container( | ||||
|                         width: 12, | ||||
|                         height: 12, | ||||
|                         decoration: const BoxDecoration(color: Colors.white, shape: BoxShape.circle), | ||||
|                         decoration: const BoxDecoration( | ||||
|                             color: Colors.white, shape: BoxShape.circle), | ||||
|                       ), | ||||
|                   ], | ||||
|                 ), | ||||
| @@ -219,14 +243,12 @@ class _HomeViewState extends State<HomeView> { | ||||
|             BoxShadow( | ||||
|                 spreadRadius: 2, | ||||
|                 blurRadius: 4, | ||||
|                 color: const Color(0xff1E293B).withOpacity(0.04) | ||||
|             ) | ||||
|           ] | ||||
|       ), | ||||
|                 color: const Color(0xff1E293B).withOpacity(0.04)) | ||||
|           ]), | ||||
|       child: Wrap( | ||||
|         spacing: 10, | ||||
|         children: listPortofolioType.asMap().entries.map((e) { | ||||
|           return  GestureDetector( | ||||
|           return GestureDetector( | ||||
|             onTap: () { | ||||
|               routePush(context, page: InvestTypeView(title: e.value.name)); | ||||
|             }, | ||||
| @@ -235,7 +257,11 @@ class _HomeViewState extends State<HomeView> { | ||||
|               width: SizeConfig.width * .18, | ||||
|               child: Column( | ||||
|                 children: [ | ||||
|                   ImageView(image: e.value.iconImage, height: SizeConfig.width * .12, width: SizeConfig.width * .12,), | ||||
|                   ImageView( | ||||
|                     image: e.value.iconImage, | ||||
|                     height: SizeConfig.width * .12, | ||||
|                     width: SizeConfig.width * .12, | ||||
|                   ), | ||||
|                   const SizedBox( | ||||
|                     height: 8, | ||||
|                   ), | ||||
| @@ -244,9 +270,7 @@ class _HomeViewState extends State<HomeView> { | ||||
|                     overflow: TextOverflow.ellipsis, | ||||
|                     textAlign: TextAlign.center, | ||||
|                     style: const TextStyle( | ||||
|                       fontSize: 12, | ||||
|                       fontWeight: FontWeight.w600 | ||||
|                     ), | ||||
|                         fontSize: 12, fontWeight: FontWeight.w600), | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
| @@ -268,24 +292,27 @@ class _HomeViewState extends State<HomeView> { | ||||
|       child: Column( | ||||
|         children: [ | ||||
|           stepVerification(), | ||||
|           const SizedBox( | ||||
|             height: 24, | ||||
|           ), | ||||
|           if(listStepVerification.currentStep == 1)...[ | ||||
|           if(listStepVerification.currentStep < 3) | ||||
|             const SizedBox( | ||||
|               height: 24, | ||||
|             ), | ||||
|           if (listStepVerification.currentStep == 1) ...[ | ||||
|             Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 const Row( | ||||
|                   children: [ | ||||
|                     Icon(Icons.verified, size: 18,), | ||||
|                     Icon( | ||||
|                       Icons.verified, | ||||
|                       size: 18, | ||||
|                       color: ColorPalette.slate300, | ||||
|                     ), | ||||
|                     SizedBox( | ||||
|                       width: 12, | ||||
|                     ), | ||||
|                     Text( | ||||
|                       'Verified by PT Gemilang', | ||||
|                       style: TextStyle( | ||||
|                           color: ColorPalette.slate500 | ||||
|                       ), | ||||
|                       style: TextStyle(color: ColorPalette.slate500), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
| @@ -294,9 +321,9 @@ class _HomeViewState extends State<HomeView> { | ||||
|                   height: 16, | ||||
|                   decoration: BoxDecoration( | ||||
|                       color: Colors.white, | ||||
|                       border: Border.all(color: const Color(0xffCBD5E1), width: 1.5), | ||||
|                       shape: BoxShape.circle | ||||
|                   ), | ||||
|                       border: Border.all( | ||||
|                           color: const Color(0xffCBD5E1), width: 1.5), | ||||
|                       shape: BoxShape.circle), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
| @@ -309,15 +336,17 @@ class _HomeViewState extends State<HomeView> { | ||||
|                 const Row( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.end, | ||||
|                   children: [ | ||||
|                     Icon(Icons.verified, size: 18,), | ||||
|                     Icon( | ||||
|                       Icons.verified, | ||||
|                       size: 18, | ||||
|                       color: ColorPalette.slate300, | ||||
|                     ), | ||||
|                     SizedBox( | ||||
|                       width: 12, | ||||
|                     ), | ||||
|                     Text( | ||||
|                       'Verified by KSEI', | ||||
|                       style: TextStyle( | ||||
|                           color: ColorPalette.slate500 | ||||
|                       ), | ||||
|                       style: TextStyle(color: ColorPalette.slate500), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
| @@ -326,9 +355,9 @@ class _HomeViewState extends State<HomeView> { | ||||
|                   height: 16, | ||||
|                   decoration: BoxDecoration( | ||||
|                       color: Colors.white, | ||||
|                       border: Border.all(color: const Color(0xffCBD5E1), width: 1.5), | ||||
|                       shape: BoxShape.circle | ||||
|                   ), | ||||
|                       border: Border.all( | ||||
|                           color: const Color(0xffCBD5E1), width: 1.5), | ||||
|                       shape: BoxShape.circle), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
| @@ -339,15 +368,12 @@ class _HomeViewState extends State<HomeView> { | ||||
|               padding: const EdgeInsets.all(12), | ||||
|               decoration: BoxDecoration( | ||||
|                   color: ColorPalette.blue50, | ||||
|                   borderRadius: BorderRadius.circular(12) | ||||
|               ), | ||||
|                   borderRadius: BorderRadius.circular(12)), | ||||
|               child: const Column( | ||||
|                 children: [ | ||||
|                   Text( | ||||
|                     'Your registration is currently being verified by PT Gemilang', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.slate500 | ||||
|                     ), | ||||
|                     style: TextStyle(color: ColorPalette.slate500), | ||||
|                   ), | ||||
|                   SizedBox( | ||||
|                     height: 16, | ||||
| @@ -355,39 +381,34 @@ class _HomeViewState extends State<HomeView> { | ||||
|                   Row( | ||||
|                     mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                     children: [ | ||||
|                       Text('Estimated:', | ||||
|                         style: TextStyle( | ||||
|                             color: ColorPalette.slate500 | ||||
|                         ), | ||||
|                       Text( | ||||
|                         'Estimated:', | ||||
|                         style: TextStyle(color: ColorPalette.slate500), | ||||
|                       ), | ||||
|                       Text( | ||||
|                         'January 30 2024', | ||||
|                         style: TextStyle( | ||||
|                             fontWeight: FontWeight.w600, | ||||
|                             color: Color(0xff1E293B) | ||||
|                         ), | ||||
|                             color: Color(0xff1E293B)), | ||||
|                       ) | ||||
|                     ], | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|             ) | ||||
|           ]else if(listStepVerification.currentStep == 0)...[ | ||||
|           ] else if (listStepVerification.currentStep == 0) ...[ | ||||
|             Container( | ||||
|               padding: const EdgeInsets.all(12), | ||||
|               decoration: BoxDecoration( | ||||
|                   color: ColorPalette.blue50, | ||||
|                   borderRadius: BorderRadius.circular(12) | ||||
|               ), | ||||
|                   borderRadius: BorderRadius.circular(12)), | ||||
|               child: Column( | ||||
|                 children: [ | ||||
|                   Text( | ||||
|                   const Text( | ||||
|                     "Let's start registering your data to start mutual fund investment at PT Gemilang Indonesia", | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.slate500 | ||||
|                     ), | ||||
|                     style: TextStyle(color: ColorPalette.slate500), | ||||
|                   ), | ||||
|                   SizedBox( | ||||
|                   const SizedBox( | ||||
|                     height: 16, | ||||
|                   ), | ||||
|                   ButtonView( | ||||
| @@ -395,9 +416,9 @@ class _HomeViewState extends State<HomeView> { | ||||
|                     width: SizeConfig.width, | ||||
|                     marginVertical: 0, | ||||
|                     heightWrapContent: true, | ||||
|                     contentPadding: EdgeInsets.all(12), | ||||
|                     contentPadding: const EdgeInsets.all(12), | ||||
|                     onPressed: () { | ||||
|                       routePush(context, page: InitialRegistrationStep()); | ||||
|                       routePush(context, page: const InitialRegistrationStep()); | ||||
|                     }, | ||||
|                   ) | ||||
|                 ], | ||||
| @@ -412,40 +433,61 @@ class _HomeViewState extends State<HomeView> { | ||||
|   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( | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: List.generate( | ||||
|         listStepVerification.nameStep.length, | ||||
|             (index) { | ||||
|           print(index % 2); | ||||
|           if (index % 2 == 0) { | ||||
|             final currentStep = index ~/ 2; | ||||
|             return 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 | ||||
|                       ) | ||||
|                     shape: BoxShape.circle, | ||||
|                     color: listStepVerification.currentStep <= currentStep | ||||
|                         ? Colors.white | ||||
|                         : const Color(0xff2563EB), | ||||
|                     border: Border.all( | ||||
|                       color: listStepVerification.currentStep < currentStep | ||||
|                           ? const Color(0xffCBD5E1) | ||||
|                           : const Color(0xff2563EB), | ||||
|                       width: 2, | ||||
|                     ), | ||||
|                   ), | ||||
|                   child: listStepVerification.currentStep <= currentStep | ||||
|                       ? const SizedBox() | ||||
|                       : const Icon( | ||||
|                     Icons.done_rounded, | ||||
|                     color: Colors.white, | ||||
|                   ), | ||||
|                   child: listStepVerification.currentStep <= e.key ? const SizedBox() : const Icon(Icons.done_rounded, color: Colors.white,), | ||||
|                 ), | ||||
|                 const SizedBox( | ||||
|                   height: 8, | ||||
|                 const SizedBox(height: 8), | ||||
|                 Text( | ||||
|                   listStepVerification.nameStep[index], | ||||
|                   style: TextStyle( | ||||
|                     color: listStepVerification.currentStep == currentStep | ||||
|                         ? const Color(0xff2563EB) | ||||
|                         : Colors.black, | ||||
|                   ), | ||||
|                 ), | ||||
|                 Text(e.value, style: TextStyle(color: listStepVerification.currentStep == e.key ? const Color(0xff2563EB) : Colors.black),) | ||||
|               ], | ||||
|             ), | ||||
|           ], | ||||
|         ); | ||||
|       }).toList(), | ||||
|             ); | ||||
|           } else { | ||||
|             return SizedBox( | ||||
|               width: 30, | ||||
|               height: 30, | ||||
|               child: Divider( | ||||
|                 color: listStepVerification.currentStep > index ~/ 2 | ||||
|                     ? const Color(0xff2563EB) | ||||
|                     : const Color(0xffCBD5E1), | ||||
|               ), | ||||
|             ); | ||||
|           } | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @@ -455,15 +497,22 @@ class _HomeViewState extends State<HomeView> { | ||||
|       child: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           const TextTitle(title: 'Info and Special Promo', color: ColorPalette.slate800,), | ||||
|           const TextTitle( | ||||
|             title: 'Info and Special Promo', | ||||
|             color: ColorPalette.slate800, | ||||
|           ), | ||||
|           const SizedBox( | ||||
|             height: 16, | ||||
|           ), | ||||
|           CarouselSlider( | ||||
|             items: [1,2,3].map((e) { | ||||
|             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,), | ||||
|                 child: ImageView( | ||||
|                   image: PathAssets.imgCarousel, | ||||
|                   height: 150, | ||||
|                   width: SizeConfig.width * .9, | ||||
|                 ), | ||||
|               ); | ||||
|             }).toList(), | ||||
|             options: CarouselOptions( | ||||
| @@ -493,14 +542,13 @@ class _HomeViewState extends State<HomeView> { | ||||
|               children: [ | ||||
|                 const TextTitle(title: 'Article', color: ColorPalette.slate800), | ||||
|                 GestureDetector( | ||||
|                   onTap: () { | ||||
|  | ||||
|                   }, | ||||
|                   child: const Text('See More', | ||||
|                   onTap: () {}, | ||||
|                   child: const Text( | ||||
|                     'See More', | ||||
|                     style: TextStyle( | ||||
|                       color: ColorPalette.primary, | ||||
|                       fontWeight: FontWeight.bold | ||||
|                     ),), | ||||
|                         color: ColorPalette.primary, | ||||
|                         fontWeight: FontWeight.bold), | ||||
|                   ), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
| @@ -511,10 +559,12 @@ class _HomeViewState extends State<HomeView> { | ||||
|           ...listArticle.asMap().entries.map((e) { | ||||
|             return Column( | ||||
|               children: [ | ||||
|                 if(e.key != 0)...[ | ||||
|                 if (e.key != 0) ...[ | ||||
|                   const Padding( | ||||
|                     padding: EdgeInsets.symmetric(vertical: 12), | ||||
|                     child: Divider(color: ColorPalette.slate200,), | ||||
|                     child: Divider( | ||||
|                       color: ColorPalette.slate200, | ||||
|                     ), | ||||
|                   ) | ||||
|                 ], | ||||
|                 cardArticle(e.value), | ||||
| @@ -531,39 +581,43 @@ class _HomeViewState extends State<HomeView> { | ||||
|       padding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|       child: Row( | ||||
|         children: [ | ||||
|           ImageView(image: PathAssets.imgArticles, width: SizeConfig.width * .17, height: SizeConfig.height * .08, borderRadius: 8,), | ||||
|           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, | ||||
|                   ), | ||||
|               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( | ||||
|               ), | ||||
|               const SizedBox( | ||||
|                 height: 8, | ||||
|               ), | ||||
|               Container( | ||||
|                 padding: | ||||
|                     const EdgeInsets.symmetric(vertical: 4, horizontal: 12), | ||||
|                 decoration: BoxDecoration( | ||||
|                     borderRadius: BorderRadius.circular(30), | ||||
|                     color: ColorPalette.green100 | ||||
|                   ), | ||||
|                   child: Text( | ||||
|                     article.type, | ||||
|                     style: TextStyle( | ||||
|                     color: ColorPalette.green100), | ||||
|                 child: Text( | ||||
|                   article.type, | ||||
|                   style: const TextStyle( | ||||
|                       fontWeight: FontWeight.w600, | ||||
|                       color: ColorPalette.green500 | ||||
|                     ), | ||||
|                   ), | ||||
|                       color: ColorPalette.green500), | ||||
|                 ), | ||||
|               ], | ||||
|             ) | ||||
|           ) | ||||
|               ), | ||||
|             ], | ||||
|           )) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   | ||||
| @@ -20,11 +20,25 @@ class InvestTypeView extends StatefulWidget { | ||||
| } | ||||
|  | ||||
| class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|    | ||||
|   List<Product> listProduct = [ | ||||
|     Product(name: 'Gemilang Dana Kas Maxima', type: '', yield: 8.17, priceUnit: 2600.79, funds: 6300000), | ||||
|     Product(name: 'Gemilang Dana Likuid', type: '', yield: 6.42, priceUnit: 1600.79, funds: 2340000), | ||||
|     Product(name: 'Gemilang Income Fund', type: '', yield: 8.17, priceUnit: 2600.79, funds: 6300000) | ||||
|     Product( | ||||
|         name: 'Gemilang Dana Kas Maxima', | ||||
|         type: '', | ||||
|         yield: 8.17, | ||||
|         priceUnit: 2600.79, | ||||
|         funds: 6300000), | ||||
|     Product( | ||||
|         name: 'Gemilang Dana Likuid', | ||||
|         type: '', | ||||
|         yield: 6.42, | ||||
|         priceUnit: 1600.79, | ||||
|         funds: 2340000), | ||||
|     Product( | ||||
|         name: 'Gemilang Income Fund', | ||||
|         type: '', | ||||
|         yield: 8.17, | ||||
|         priceUnit: 2600.79, | ||||
|         funds: 6300000) | ||||
|   ]; | ||||
|  | ||||
|   @override | ||||
| @@ -36,76 +50,80 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|       return e; | ||||
|     }).toList(); | ||||
|   } | ||||
|    | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ChangeNotifierProvider<ProductViewModel>( | ||||
|       create: (context) => ProductViewModel(), | ||||
|       child: Consumer<ProductViewModel>( | ||||
|         builder: (context, provider, child) { | ||||
|           return Scaffold( | ||||
|             body: SizedBox( | ||||
|               width: SizeConfig.width, | ||||
|               height: SizeConfig.height, | ||||
|               child: Stack( | ||||
|                 children: [ | ||||
|                   const ImageView(image: PathAssets.imgDashboardAccount), | ||||
|                   Column( | ||||
|                     children: [ | ||||
|                       const SizedBox( | ||||
|                         height: 50, | ||||
|       child: Consumer<ProductViewModel>(builder: (context, provider, child) { | ||||
|         return Scaffold( | ||||
|           body: SizedBox( | ||||
|             width: SizeConfig.width, | ||||
|             height: SizeConfig.height, | ||||
|             child: Stack( | ||||
|               children: [ | ||||
|                 const ImageView(image: PathAssets.imgDashboardAccount), | ||||
|                 Column( | ||||
|                   children: [ | ||||
|                     SizedBox( | ||||
|                       height: SizeConfig.height * .1, | ||||
|                     ), | ||||
|                     Padding( | ||||
|                       padding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                       child: Row( | ||||
|                         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                         children: [ | ||||
|                           const BackButtonView(), | ||||
|                           TextTitle(title: widget.title, color: Colors.white), | ||||
|                           SizedBox( | ||||
|                             width: SizeConfig.width * 0.1, | ||||
|                           ) | ||||
|                         ], | ||||
|                       ), | ||||
|                       Padding( | ||||
|                         padding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                         child: Row( | ||||
|                           mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                           children: [ | ||||
|                             const BackButtonView(), | ||||
|                             TextTitle(title: widget.title, color: Colors.white), | ||||
|                             SizedBox( | ||||
|                               width: SizeConfig.width * 0.1, | ||||
|                             ) | ||||
|                           ], | ||||
|                         ), | ||||
|                       ), | ||||
|                       const SizedBox( | ||||
|                         height: 24, | ||||
|                       ), | ||||
|                       Container( | ||||
|                     ), | ||||
|                     const SizedBox( | ||||
|                       height: 24, | ||||
|                     ), | ||||
|                     Expanded( | ||||
|                       child: Container( | ||||
|                         padding: const EdgeInsets.all(24), | ||||
|                         decoration: BoxDecoration( | ||||
|                           borderRadius: BorderRadius.circular(16), | ||||
|                           color: Colors.white | ||||
|                         ), | ||||
|                         child: Column( | ||||
|                           children: [ | ||||
|                             filters(), | ||||
|                             ListView( | ||||
|                               shrinkWrap: true, | ||||
|                               children: listProduct.asMap().entries.map((e) { | ||||
|                                 return GestureDetector( | ||||
|                                   onTap: () { | ||||
|                                     provider.setSelectedProduct(e.value); | ||||
|                                     routePush(context, page: ProductView(widget.title)); | ||||
|                                   }, | ||||
|                                   child: Padding( | ||||
|                                     padding: EdgeInsets.only(top: e.key != 0 ? 24 : 0), | ||||
|                                     child: cardProduct(e.value), | ||||
|                                   ), | ||||
|                                 ); | ||||
|                               }).toList(), | ||||
|                             ) | ||||
|                           ], | ||||
|                             borderRadius: BorderRadius.circular(16), | ||||
|                             color: Colors.white), | ||||
|                         child: SingleChildScrollView( | ||||
|                           scrollDirection: Axis.vertical, | ||||
|                           child: Column( | ||||
|                             children: [ | ||||
|                               filters(), | ||||
|                               ListView( | ||||
|                                 shrinkWrap: true, | ||||
|                                 children: listProduct.asMap().entries.map((e) { | ||||
|                                   return GestureDetector( | ||||
|                                     onTap: () { | ||||
|                                       provider.setSelectedProduct(e.value); | ||||
|                                       routePush(context, | ||||
|                                           page: ProductView(selectedProduct: e.value)); | ||||
|                                     }, | ||||
|                                     child: Padding( | ||||
|                                       padding: EdgeInsets.only( | ||||
|                                           top: e.key != 0 ? 24 : 0), | ||||
|                                       child: cardProduct(e.value), | ||||
|                                     ), | ||||
|                                   ); | ||||
|                                 }).toList(), | ||||
|                               ) | ||||
|                             ], | ||||
|                           ), | ||||
|                         ), | ||||
|                       ), | ||||
|                     ], | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|                     ), | ||||
|                   ], | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ); | ||||
|         } | ||||
|       ), | ||||
|           ), | ||||
|         ); | ||||
|       }), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @@ -115,9 +133,25 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|       child: Row( | ||||
|         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|         children: [ | ||||
|           segmentFilter(const Icon(Icons.filter_alt_outlined, color: ColorPalette.slate400,), 'Filter', () { }), | ||||
|           segmentFilter(const RotatedBox(quarterTurns: 1, child: Icon(Icons.compare_arrows, color: ColorPalette.slate400)), 'Sort', () { }), | ||||
|           segmentFilter(const Icon(Icons.dashboard_outlined, color: ColorPalette.slate400), 'Compare', () { }), | ||||
|           segmentFilter( | ||||
|               const Icon( | ||||
|                 Icons.filter_alt_outlined, | ||||
|                 color: ColorPalette.slate400, | ||||
|               ), | ||||
|               'Filter', | ||||
|               () {}), | ||||
|           segmentFilter( | ||||
|               const RotatedBox( | ||||
|                   quarterTurns: 1, | ||||
|                   child: | ||||
|                       Icon(Icons.compare_arrows, color: ColorPalette.slate400)), | ||||
|               'Sort', | ||||
|               () {}), | ||||
|           segmentFilter( | ||||
|               const Icon(Icons.dashboard_outlined, | ||||
|                   color: ColorPalette.slate400), | ||||
|               'Compare', | ||||
|               () {}), | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
| @@ -126,12 +160,11 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|   Widget segmentFilter(Widget leading, String text, void Function()? onTap) { | ||||
|     return GestureDetector( | ||||
|       onTap: onTap, | ||||
|       child:  Container( | ||||
|       child: Container( | ||||
|         padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16), | ||||
|         decoration: BoxDecoration( | ||||
|             border: Border.all(color: ColorPalette.slate200), | ||||
|             borderRadius: BorderRadius.circular(56) | ||||
|         ), | ||||
|             borderRadius: BorderRadius.circular(56)), | ||||
|         child: Row( | ||||
|           children: [ | ||||
|             leading, | ||||
| @@ -142,9 +175,7 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|               text, | ||||
|               overflow: TextOverflow.ellipsis, | ||||
|               style: const TextStyle( | ||||
|                 color: ColorPalette.slate500, | ||||
|                 fontWeight: FontWeight.w700 | ||||
|               ), | ||||
|                   color: ColorPalette.slate500, fontWeight: FontWeight.w700), | ||||
|             ) | ||||
|           ], | ||||
|         ), | ||||
| @@ -163,7 +194,10 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|         children: [ | ||||
|           Row( | ||||
|             children: [ | ||||
|               ImageView(image: PathAssets.imgProduct, width: SizeConfig.width * .12,), | ||||
|               ImageView( | ||||
|                 image: PathAssets.imgProduct, | ||||
|                 width: SizeConfig.width * .12, | ||||
|               ), | ||||
|               const SizedBox( | ||||
|                 width: 8, | ||||
|               ), | ||||
| @@ -171,9 +205,7 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|                 child: Text( | ||||
|                   product.name ?? '', | ||||
|                   style: const TextStyle( | ||||
|                     fontWeight: FontWeight.bold, | ||||
|                     fontSize: 18 | ||||
|                   ), | ||||
|                       fontWeight: FontWeight.bold, fontSize: 18), | ||||
|                 ), | ||||
|               ) | ||||
|             ], | ||||
| @@ -187,36 +219,53 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|             children: [ | ||||
|               Column( | ||||
|                 children: [ | ||||
|                   const Text('Yield', style: TextStyle(color: ColorPalette.slate400, fontWeight: FontWeight.w600),), | ||||
|                   const Text( | ||||
|                     'Yield', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontWeight: FontWeight.w600), | ||||
|                   ), | ||||
|                   Row( | ||||
|                     children: [ | ||||
|                       Text( | ||||
|                         '${product.yield.toString()}%', | ||||
|                         style: const TextStyle( | ||||
|                           color: ColorPalette.green400, | ||||
|                           fontWeight: FontWeight.w600 | ||||
|                         ), | ||||
|                             color: ColorPalette.green400, | ||||
|                             fontWeight: FontWeight.w600), | ||||
|                       ), | ||||
|                       const Text('/'), | ||||
|                       const Text('3year', style: TextStyle(color: ColorPalette.slate400, fontWeight: FontWeight.w600),) | ||||
|                       const Text( | ||||
|                         '3year', | ||||
|                         style: TextStyle( | ||||
|                             color: ColorPalette.slate400, | ||||
|                             fontWeight: FontWeight.w600), | ||||
|                       ) | ||||
|                     ], | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|               Column( | ||||
|                 children: [ | ||||
|                   const Text('Price/unit', style: TextStyle(color: ColorPalette.slate400, fontWeight: FontWeight.w600),), | ||||
|                   const Text( | ||||
|                     'Price/unit', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontWeight: FontWeight.w600), | ||||
|                   ), | ||||
|                   Row( | ||||
|                     children: [ | ||||
|                       const Icon(Icons.trending_up_outlined, size: 18, color: ColorPalette.green400,), | ||||
|                       const Icon( | ||||
|                         Icons.trending_up_outlined, | ||||
|                         size: 18, | ||||
|                         color: ColorPalette.green400, | ||||
|                       ), | ||||
|                       const SizedBox( | ||||
|                         width: 2, | ||||
|                       ), | ||||
|                       Text( | ||||
|                         NumberFormatter.numberCurrency(product.priceUnit, 'Rp', 'id_ID'), | ||||
|                         style: const TextStyle( | ||||
|                           fontWeight: FontWeight.w600 | ||||
|                         ), | ||||
|                         NumberFormatter.numberCurrency( | ||||
|                             product.priceUnit, 'Rp', 'id_ID'), | ||||
|                         style: const TextStyle(fontWeight: FontWeight.w600), | ||||
|                       ), | ||||
|                     ], | ||||
|                   ) | ||||
| @@ -224,14 +273,18 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|               ), | ||||
|               Column( | ||||
|                 children: [ | ||||
|                   const Text('Managed funds', style: TextStyle(color: ColorPalette.slate400, fontWeight: FontWeight.w600),), | ||||
|                   const Text( | ||||
|                     'Managed funds', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontWeight: FontWeight.w600), | ||||
|                   ), | ||||
|                   Row( | ||||
|                     children: [ | ||||
|                       Text( | ||||
|                         NumberFormatter.compactCurrency(product.funds, 'Rp ', 'id_ID'), | ||||
|                         style: const TextStyle( | ||||
|                             fontWeight: FontWeight.w600 | ||||
|                         ), | ||||
|                         NumberFormatter.compactCurrency( | ||||
|                             product.funds, 'Rp ', 'id_ID'), | ||||
|                         style: const TextStyle(fontWeight: FontWeight.w600), | ||||
|                       ), | ||||
|                     ], | ||||
|                   ) | ||||
| @@ -243,5 +296,4 @@ class _InvestTypeViewState extends State<InvestTypeView> { | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,12 +1,8 @@ | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/custom_app_bar/custom_app_bar.dart'; | ||||
| import 'package:cims_apps/application/component/subscribe/goal_investing_view.dart'; | ||||
| import 'package:cims_apps/application/component/subscribe/input_investment_view.dart'; | ||||
| import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart'; | ||||
| import 'package:cims_apps/application/component/risk_profile.dart'; | ||||
| import 'package:cims_apps/application/component/text_form/text_form_view.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/utils/number_formatter.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/options_starting_invest.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart'; | ||||
| @@ -40,42 +36,49 @@ class _PlanViewState extends State<PlanView> { | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|  | ||||
|     return Scaffold( | ||||
|       appBar: CustomAppBar(height: 70, title: 'Investment Plan'), | ||||
|       body: SingleChildScrollView( | ||||
|         padding: EdgeInsets.all(24), | ||||
|         child: Column( | ||||
|           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|           children: [ | ||||
|             RiskProfile( | ||||
|               totalScore: 26, | ||||
|               rowSuitableProduct: true | ||||
|             ), | ||||
|             SizedBox( | ||||
|               height: 32, | ||||
|             ), | ||||
|             Text('Your Goal in Investing', | ||||
|               style: TextStyle( | ||||
|                 fontWeight: FontWeight.w700, | ||||
|                 color: ColorPalette.slate800, | ||||
|                 fontSize: 18 | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => PlanViewModel(), | ||||
|       child: Scaffold( | ||||
|         appBar: CustomAppBar( | ||||
|           height: SizeConfig.height * 0.08, | ||||
|           title: 'Investment Plan', | ||||
|           leading: const SizedBox(), | ||||
|         ), | ||||
|         body: SingleChildScrollView( | ||||
|           padding: const EdgeInsets.all(24), | ||||
|           child: Column( | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               const RiskProfile( | ||||
|                 totalScore: 26, | ||||
|                 rowSuitableProduct: true | ||||
|               ), | ||||
|             ), | ||||
|             SizedBox( | ||||
|               height: 24, | ||||
|             ), | ||||
|             GoalInvestingView( | ||||
|               onListSelected: (p0) { | ||||
|                 showModalBottomSheet( | ||||
|                   context: context, | ||||
|                   isScrollControlled: true, | ||||
|                   builder: (context) { | ||||
|                     return modalInvest(context, p0); | ||||
|                   }, | ||||
|                 ); | ||||
|               }, | ||||
|             ) | ||||
|           ], | ||||
|               const SizedBox( | ||||
|                 height: 32, | ||||
|               ), | ||||
|               const Text('Your Goal in Investing', | ||||
|                 style: TextStyle( | ||||
|                   fontWeight: FontWeight.w700, | ||||
|                   color: ColorPalette.slate800, | ||||
|                   fontSize: 18 | ||||
|                 ), | ||||
|               ), | ||||
|               const SizedBox( | ||||
|                 height: 24, | ||||
|               ), | ||||
|               GoalInvestingView( | ||||
|                 onListSelected: (p0) { | ||||
|                   showModalBottomSheet( | ||||
|                     context: context, | ||||
|                     isScrollControlled: true, | ||||
|                     builder: (context) { | ||||
|                       return modalInvest(context, p0); | ||||
|                     }, | ||||
|                   ); | ||||
|                 }, | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
| @@ -95,7 +98,7 @@ class _PlanViewState extends State<PlanView> { | ||||
|             child: Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 Text("It's time to invest", | ||||
|                 const Text("It's time to invest", | ||||
|                   style: TextStyle( | ||||
|                     fontSize: 16, | ||||
|                     fontWeight: FontWeight.w600 | ||||
| @@ -105,17 +108,20 @@ class _PlanViewState extends State<PlanView> { | ||||
|                   onTap: () { | ||||
|                     Navigator.pop(context); | ||||
|                   }, | ||||
|                   child: Icon(Icons.close_rounded) | ||||
|                   child: const Icon(Icons.close_rounded) | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           Divider(color: ColorPalette.slate200, height: 1), | ||||
|           const Divider(color: ColorPalette.slate200, height: 1), | ||||
|           InputInvestmentView( | ||||
|             selectedPlan: text, | ||||
|             currentPlan: text, | ||||
|             changePlan: () { | ||||
|               Navigator.pop(context); | ||||
|             }, | ||||
|             nextMove: (value) { | ||||
|               Navigator.pop(context); | ||||
|               int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll(',', '')); | ||||
|               int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll('.', '')); | ||||
|               showModalBottomSheet(context: context, builder: (context) => OptionsStartingInvest(totalInvest: formatIntParse)); | ||||
|             }, | ||||
|           ), | ||||
|   | ||||
| @@ -3,9 +3,14 @@ import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/subscribe/total_payment_view.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/core/route/route.dart'; | ||||
| import 'package:cims_apps/core/utils/size_config.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view/step_invest_plan/options_starting_invest.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view/product_view.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class ResultOptionsProduct extends StatelessWidget { | ||||
|   final int totalInvest; | ||||
| @@ -19,134 +24,169 @@ class ResultOptionsProduct extends StatelessWidget { | ||||
|       Product(name: 'Gemilang Kas 2 Kelas A', type: 'Shares', totalPercent: 0.1) | ||||
|     ]; | ||||
|  | ||||
|     return Container( | ||||
|       decoration: BoxDecoration( | ||||
|         color: Colors.white, | ||||
|         borderRadius: BorderRadius.circular(16) | ||||
|       ), | ||||
|       padding: const EdgeInsets.all(24), | ||||
|       child: Column( | ||||
|         mainAxisSize: MainAxisSize.min, | ||||
|         children: [ | ||||
|           const Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|             children: [ | ||||
|               Icon(Icons.arrow_back, color: ColorPalette.slate500), | ||||
|               Text('Results from your risk profile', | ||||
|                 style: TextStyle( | ||||
|                   fontWeight: FontWeight.w600, | ||||
|                   fontSize: 18, | ||||
|                   color: ColorPalette.slate800 | ||||
|                 ), | ||||
|               ), | ||||
|               Icon(Icons.close_rounded, color: ColorPalette.slate400) | ||||
|             ], | ||||
|           ), | ||||
|           const SizedBox(height: 32), | ||||
|           SingleChildScrollView( | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => PlanViewModel(), | ||||
|       child: Consumer<PlanViewModel>( | ||||
|         builder: (context, provider, child) { | ||||
|           return Container( | ||||
|             decoration: BoxDecoration( | ||||
|               color: Colors.white, | ||||
|               borderRadius: BorderRadius.circular(16) | ||||
|             ), | ||||
|             padding: const EdgeInsets.all(24), | ||||
|             child: Column( | ||||
|               children: listProduct.asMap().entries.map((e) { | ||||
|                 return Container( | ||||
|                   margin: const EdgeInsets.only(bottom: 16), | ||||
|                   padding: const EdgeInsets.all(16), | ||||
|                   decoration: BoxDecoration( | ||||
|                       color: Colors.white, | ||||
|                       border: Border.all(color: ColorPalette.slate200), | ||||
|                       borderRadius: BorderRadius.circular(12), | ||||
|                       boxShadow: const [ | ||||
|                         BoxShadow( | ||||
|                             color: Color(0XFF1E293B0A) | ||||
|                         ) | ||||
|                       ] | ||||
|                   ), | ||||
|               mainAxisSize: MainAxisSize.min, | ||||
|               children: [ | ||||
|                 Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     GestureDetector( | ||||
|                         onTap: () { | ||||
|                           Navigator.pop(context); | ||||
|                           showModalBottomSheet( | ||||
|                             context: context, | ||||
|                             builder: (context) { | ||||
|                               return OptionsStartingInvest(totalInvest: totalInvest); | ||||
|                             }, | ||||
|                           ); | ||||
|                         }, | ||||
|                       child: Icon(Icons.arrow_back, color: ColorPalette.slate500) | ||||
|                     ), | ||||
|                     Text('Results from your risk profile', | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         fontSize: 18, | ||||
|                         color: ColorPalette.slate800 | ||||
|                       ), | ||||
|                     ), | ||||
|                     GestureDetector( | ||||
|                       onTap: () { | ||||
|                         Navigator.pop(context); | ||||
|  | ||||
|                       }, | ||||
|                       child: Icon(Icons.close_rounded, color: ColorPalette.slate400) | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|                 const SizedBox(height: 32), | ||||
|                 SingleChildScrollView( | ||||
|                   child: Column( | ||||
|                     children: [ | ||||
|                       Row( | ||||
|                         children: [ | ||||
|                           const ImageView(image: PathAssets.iconGoogle, width: 30,), | ||||
|                           const SizedBox( | ||||
|                             width: 12, | ||||
|                           ), | ||||
|                           Expanded( | ||||
|                             child: Column( | ||||
|                               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                     children: listProduct.asMap().entries.map((e) { | ||||
|                       return Container( | ||||
|                         margin: const EdgeInsets.only(bottom: 16), | ||||
|                         padding: const EdgeInsets.all(16), | ||||
|                         decoration: BoxDecoration( | ||||
|                             color: Colors.white, | ||||
|                             border: Border.all(color: ColorPalette.slate200), | ||||
|                             borderRadius: BorderRadius.circular(12), | ||||
|                             boxShadow: const [ | ||||
|                               BoxShadow( | ||||
|                                   color: Color(0XFF1E293B0A) | ||||
|                               ) | ||||
|                             ] | ||||
|                         ), | ||||
|                         child: Column( | ||||
|                           children: [ | ||||
|                             Row( | ||||
|                               children: [ | ||||
|                                 Text(e.value.name ?? '', | ||||
|                                   style: const TextStyle( | ||||
|                                     fontWeight: FontWeight.w700, | ||||
|                                     color: ColorPalette.slate800, | ||||
|                                 const ImageView(image: PathAssets.iconGoogle, width: 30,), | ||||
|                                 const SizedBox( | ||||
|                                   width: 12, | ||||
|                                 ), | ||||
|                                 Expanded( | ||||
|                                   child: Column( | ||||
|                                     crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                                     children: [ | ||||
|                                       Text(e.value.name ?? '', | ||||
|                                         style: const TextStyle( | ||||
|                                           fontWeight: FontWeight.w700, | ||||
|                                           color: ColorPalette.slate800, | ||||
|                                         ), | ||||
|                                       ), | ||||
|                                       const SizedBox(height: 4,), | ||||
|                                       Container( | ||||
|                                         padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12), | ||||
|                                         decoration: BoxDecoration( | ||||
|                                             border: Border.all(color: ColorPalette.investTypeColor[e.value.type]!), | ||||
|                                             color: ColorPalette.investTypeBgColor[e.value.type], | ||||
|                                             borderRadius: BorderRadius.circular(40) | ||||
|                                         ), | ||||
|                                         child: Text(e.value.type ?? '', | ||||
|                                           style: TextStyle( | ||||
|                                             color: ColorPalette.investTypeColor[e.value.type], | ||||
|                                             fontWeight: FontWeight.w600, | ||||
|                                             fontSize: 12 | ||||
|                                           ), | ||||
|                                         ), | ||||
|                                       ) | ||||
|                                     ], | ||||
|                                   ), | ||||
|                                 ), | ||||
|                                 const SizedBox(height: 4,), | ||||
|                                 Container( | ||||
|                                   padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12), | ||||
|                                   decoration: BoxDecoration( | ||||
|                                       border: Border.all(color: ColorPalette.investTypeColor[e.value.type]!), | ||||
|                                       color: ColorPalette.investTypeBgColor[e.value.type], | ||||
|                                       borderRadius: BorderRadius.circular(40) | ||||
|                                   ), | ||||
|                                   child: Text(e.value.type ?? '', | ||||
|                                     style: TextStyle( | ||||
|                                       color: ColorPalette.investTypeColor[e.value.type], | ||||
|                                       fontWeight: FontWeight.w600, | ||||
|                                       fontSize: 12 | ||||
|                                     ), | ||||
|                                 Text('${(e.value.totalPercent! * 100).toInt()} %', | ||||
|                                   style: const TextStyle( | ||||
|                                       fontWeight: FontWeight.w700, | ||||
|                                       fontSize: 16, | ||||
|                                       color: ColorPalette.slate800 | ||||
|                                   ), | ||||
|                                 ) | ||||
|                               ], | ||||
|                             ), | ||||
|                           ), | ||||
|                           Text('${(e.value.totalPercent! * 100).toInt()} %', | ||||
|                             style: const TextStyle( | ||||
|                                 fontWeight: FontWeight.w700, | ||||
|                                 fontSize: 16, | ||||
|                                 color: ColorPalette.slate800 | ||||
|                             const Padding( | ||||
|                               padding: EdgeInsets.symmetric(vertical: 16), | ||||
|                               child: Divider(height: 1, color: ColorPalette.slate200), | ||||
|                             ), | ||||
|                           ) | ||||
|                         ], | ||||
|                       ), | ||||
|                       const Padding( | ||||
|                         padding: EdgeInsets.symmetric(vertical: 16), | ||||
|                         child: Divider(height: 1, color: ColorPalette.slate200), | ||||
|                       ), | ||||
|                       GestureDetector( | ||||
|                         onTap: () { | ||||
|                         }, | ||||
|                         child: const Text('See More', | ||||
|                           style: TextStyle( | ||||
|                             color: ColorPalette.slate500, | ||||
|                             fontWeight: FontWeight.w600, | ||||
|                           ), | ||||
|                             GestureDetector( | ||||
|                               onTap: () { | ||||
|                                 routePush(context, page: ProductView(selectedProduct: e.value, seeMore: true)); | ||||
|                               }, | ||||
|                               child: const Text('See More', | ||||
|                                 style: TextStyle( | ||||
|                                   color: ColorPalette.slate500, | ||||
|                                   fontWeight: FontWeight.w600, | ||||
|                                 ), | ||||
|                               ), | ||||
|                             ) | ||||
|                           ], | ||||
|                         ), | ||||
|                       ) | ||||
|                     ], | ||||
|                       ); | ||||
|                     }).toList(), | ||||
|                   ), | ||||
|                 ); | ||||
|               }).toList(), | ||||
|                 ), | ||||
|                 const SizedBox( | ||||
|                   height: 16, | ||||
|                 ), | ||||
|                 ButtonView( | ||||
|                   name: 'Next', | ||||
|                   onPressed: () { | ||||
|                     Navigator.pop(context); | ||||
|                     showModalBottomSheet( | ||||
|                       context: context, | ||||
|                       isScrollControlled: true, | ||||
|                       builder: (context) => | ||||
|                         ChangeNotifierProvider( | ||||
|                           create: (context) => PlanViewModel(), | ||||
|                           child: Consumer<PlanViewModel>( | ||||
|                             builder: (context, planProvider, _) { | ||||
|                               return TotalPaymentView( | ||||
|                                 listProduct: listProduct, | ||||
|                                 totalInvest: totalInvest, | ||||
|                                 isAgree: planProvider.isAgree, | ||||
|                                 onTapAgree: planProvider.setAgree, | ||||
|                               ); | ||||
|                             } | ||||
|                           ), | ||||
|                         ) | ||||
|                     ); | ||||
|                   }, | ||||
|                   width: SizeConfig.width, | ||||
|                   heightWrapContent: true, | ||||
|                   contentPadding: const EdgeInsets.symmetric(vertical: 16), | ||||
|                   marginVertical: 0, | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           const SizedBox( | ||||
|             height: 16, | ||||
|           ), | ||||
|           ButtonView( | ||||
|             name: 'Next', | ||||
|             onPressed: () { | ||||
|               showModalBottomSheet( | ||||
|                   context: context, | ||||
|                   isScrollControlled: true, | ||||
|                   builder: (context) => | ||||
|                     TotalPaymentView( | ||||
|                       listProduct: listProduct, | ||||
|                       totalInvest: totalInvest, | ||||
|                     ) | ||||
|               ); | ||||
|             }, | ||||
|             width: SizeConfig.width, | ||||
|             heightWrapContent: true, | ||||
|             contentPadding: const EdgeInsets.symmetric(vertical: 16), | ||||
|             marginVertical: 0, | ||||
|           ) | ||||
|         ], | ||||
|           ); | ||||
|         } | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|   | ||||
| @@ -3,4 +3,10 @@ import 'package:flutter/material.dart'; | ||||
|  | ||||
| class PlanViewModel extends ChangeNotifier { | ||||
|   List<Product> listProduct = []; | ||||
|   bool isAgree = false; | ||||
|  | ||||
|   void setAgree() { | ||||
|     isAgree = !isAgree; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,265 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/back_button_view.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_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/portfolio/redeem_product/view/redeem_product.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class PortfolioDetailView extends StatelessWidget { | ||||
|   const PortfolioDetailView({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     List<PortfolioProduct> listProduct = [ | ||||
|       PortfolioProduct( | ||||
|           name: 'Gemilang Dana Kas Maxima', | ||||
|           type: 'Money Market', | ||||
|           yield: 8.17, | ||||
|           priceUnit: 2600.79, | ||||
|           funds: 6300000, | ||||
|           totalUnit: 14520 | ||||
|       ), | ||||
|       PortfolioProduct( | ||||
|           name: 'Gemilang Dana Likuid', | ||||
|           type: 'Sharia', | ||||
|           yield: 6.42, | ||||
|           priceUnit: 1600.79, | ||||
|           funds: 2340000, | ||||
|           totalUnit: 232, | ||||
|       ), | ||||
|       PortfolioProduct( | ||||
|           name: 'Gemilang Income Fund', | ||||
|           type: 'Bonds', | ||||
|           yield: 8.17, | ||||
|           priceUnit: 2600.79, | ||||
|           funds: 6300000, | ||||
|           totalUnit: 2450, | ||||
|       ) | ||||
|     ]; | ||||
|  | ||||
|  | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => RedeemProductViewModel(), | ||||
|       child: 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, | ||||
|                   ), | ||||
|                   Padding( | ||||
|                     padding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                     child: Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         const BackButtonView(), | ||||
|                         const TextTitle(title: 'Education', color: Colors.white), | ||||
|                         SizedBox(width: SizeConfig.width * 0.1,) | ||||
|                       ], | ||||
|                     ), | ||||
|                   ), | ||||
|                   const SizedBox(height: 24,), | ||||
|                   const Padding( | ||||
|                     padding: EdgeInsets.symmetric(horizontal: 24), | ||||
|                     child: Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         Column( | ||||
|                           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                           children: [ | ||||
|                             Text('Portfolio Value', | ||||
|                               style: TextStyle( | ||||
|                                   color: ColorPalette.white, | ||||
|                                   fontWeight: FontWeight.w400 | ||||
|                               ), | ||||
|                             ), | ||||
|                             TextTitle(title: 'Rp 2.000.000', fontSize: 14, color: Colors.white) | ||||
|                           ], | ||||
|                         ), | ||||
|                         Column( | ||||
|                           crossAxisAlignment: CrossAxisAlignment.end, | ||||
|                           children: [ | ||||
|                             Text('Advantages', | ||||
|                               style: TextStyle( | ||||
|                                   color: ColorPalette.white, | ||||
|                                   fontWeight: FontWeight.w400 | ||||
|                               ), | ||||
|                             ), | ||||
|                             TextTitle(title: 'Rp 2.000.000', fontSize: 14, color: Colors.white) | ||||
|                           ], | ||||
|                         ) | ||||
|                       ], | ||||
|                     ), | ||||
|                   ), | ||||
|                   const SizedBox(height: 24,), | ||||
|                   Expanded( | ||||
|                     child: Container( | ||||
|                       decoration: BoxDecoration( | ||||
|                         borderRadius: BorderRadius.circular(12), | ||||
|                         color: Colors.white, | ||||
|                       ), | ||||
|                       child: Consumer<RedeemProductViewModel>( | ||||
|                         builder: (context, provider, child) { | ||||
|                           return ListView( | ||||
|                             padding: const EdgeInsets.all(24), | ||||
|                             children: listProduct.asMap().entries.map((e) { | ||||
|                               return cardPortfolio(context, e.value); | ||||
|                             }).toList(), | ||||
|                           ); | ||||
|                         } | ||||
|                       ), | ||||
|                     ) | ||||
|                   ) | ||||
|                 ], | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget cardPortfolio(context, PortfolioProduct product) { | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.all(16), | ||||
|       decoration: BoxDecoration( | ||||
|         color: Colors.white, | ||||
|         borderRadius: BorderRadius.circular(12), | ||||
|         border: Border.all(color: ColorPalette.slate200), | ||||
|         boxShadow: [ | ||||
|           BoxShadow( | ||||
|             color: const Color(0xff1E293B).withOpacity(0.04), | ||||
|             blurRadius: 8, | ||||
|             spreadRadius: 2 | ||||
|           ) | ||||
|         ] | ||||
|       ), | ||||
|       child: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           Row( | ||||
|             crossAxisAlignment: CrossAxisAlignment.start, | ||||
|             children: [ | ||||
|               ImageView( | ||||
|                 image: PathAssets.imgProduct, | ||||
|                 width: SizeConfig.width * .13, | ||||
|               ), | ||||
|               const SizedBox(width: 8), | ||||
|               Expanded( | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     TextTitle(title: product.name ?? '', fontSize: 16,), | ||||
|                     const SizedBox(height: 4), | ||||
|                     Container( | ||||
|                       padding: const EdgeInsets.all(6), | ||||
|                       decoration: BoxDecoration( | ||||
|                           borderRadius: BorderRadius.circular(40), | ||||
|                           color: ColorPalette.investTypeBgColor[product.type!]?.withOpacity(0.5) ?? Colors.white, | ||||
|                           border: Border.all(width: 2, color: ColorPalette.investTypeColor[product.type!]?.withOpacity(0.4) ?? Colors.white) | ||||
|                       ), | ||||
|                       child: Text( | ||||
|                         product.type ?? '', | ||||
|                         style: TextStyle( | ||||
|                             color: ColorPalette.investTypeColor[product.type!], | ||||
|                             fontWeight: FontWeight.w600 | ||||
|                         ), | ||||
|                       ), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|           const Padding( | ||||
|             padding: EdgeInsets.symmetric(vertical: 16), | ||||
|             child: Divider(height: 1, color: ColorPalette.slate200,), | ||||
|           ), | ||||
|           Wrap( | ||||
|             runSpacing: 8, | ||||
|             children: [ | ||||
|               rowDescription('Present Value', 'Rp2.660.706', fontWeight: FontWeight.w700), | ||||
|               rowDescription('Investment Capital', 'Rp2.660.706'), | ||||
|               rowDescription('Advantages', 'Rp2.660.706'), | ||||
|               rowDescription('Purchase Price', NumberFormatter.numberCurrency(product.priceUnit, 'Rp ', 'id_ID')), | ||||
|               rowDescription('Number of Units', '${product.totalUnit ?? 0}'), | ||||
|             ], | ||||
|           ), | ||||
|           const SizedBox(height: 16,), | ||||
|           Row( | ||||
|             children: [ | ||||
|               Expanded( | ||||
|                 child: ButtonView( | ||||
|                   name: 'Redeem', | ||||
|                   marginVertical: 0, | ||||
|                   width: SizeConfig.width, | ||||
|                   isOutlined: true, | ||||
|                   heightWrapContent: true, | ||||
|                   contentPadding: const EdgeInsets.all(12), | ||||
|                   backgroundColor: Colors.white, | ||||
|                   borderColor: ColorPalette.red600, | ||||
|                   textColor: ColorPalette.red600, | ||||
|                   onPressed: () { | ||||
|                     Provider.of<RedeemProductViewModel>(context, listen: false).setProduct(product); | ||||
|                     showModalBottomSheet( | ||||
|                       context: context, | ||||
|                       isScrollControlled: true, | ||||
|                       builder: (context) { | ||||
|                         return RedeemProduct(); | ||||
|                       }, | ||||
|                     ); | ||||
|                   }, | ||||
|                 ), | ||||
|               ), | ||||
|               const SizedBox(width: 16), | ||||
|               Expanded( | ||||
|                 child: ButtonView( | ||||
|                   name: 'Buy', | ||||
|                   marginVertical: 0, | ||||
|                   width: SizeConfig.width, | ||||
|                   heightWrapContent: true, | ||||
|                   contentPadding: const EdgeInsets.all(12), | ||||
|                   onPressed: () { | ||||
|  | ||||
|                   }, | ||||
|                 ), | ||||
|               ) | ||||
|             ], | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget rowDescription(String title, String value, {Color? color, FontWeight? fontWeight}) { | ||||
|     return Row( | ||||
|       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|       children: [ | ||||
|         Text(title, | ||||
|           style: const TextStyle( | ||||
|             color: ColorPalette.slate400, | ||||
|             fontWeight: FontWeight.w600 | ||||
|           ), | ||||
|         ), | ||||
|         Text(value, | ||||
|           style: TextStyle( | ||||
|             color: color ?? ColorPalette.slate800, | ||||
|             fontWeight: fontWeight ?? FontWeight.w600, | ||||
|           ), | ||||
|         ) | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,9 +1,10 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| 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/portfolio/portfolio_detail_view.dart'; | ||||
| import 'package:fl_chart/fl_chart.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| @@ -70,7 +71,7 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|                 ), | ||||
|                 const Center( | ||||
|                   child: Text( | ||||
|                     'Portofolio', | ||||
|                     'Portfolio', | ||||
|                     style: TextStyle( | ||||
|                         fontSize: 20, | ||||
|                         fontWeight: FontWeight.w700, | ||||
| @@ -125,20 +126,22 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|                           ), | ||||
|                           Column( | ||||
|                             children: [ | ||||
|                               const Text('Total Mutual Fund', | ||||
|                               const Text( | ||||
|                                 'Total Mutual Fund', | ||||
|                                 style: TextStyle( | ||||
|                                   fontWeight: FontWeight.w600, | ||||
|                                   fontSize: 18, | ||||
|                                   color: ColorPalette.slate400 | ||||
|                                 ), | ||||
|                                     fontWeight: FontWeight.w600, | ||||
|                                     fontSize: 18, | ||||
|                                     color: ColorPalette.slate400), | ||||
|                               ), | ||||
|                               Text(listInvestmentType.map((e) => e.mutualFunds).reduce((value, element) => value + element).toString(), | ||||
|                                 style: const TextStyle( | ||||
|                                     fontSize: 44, | ||||
|                                     fontWeight: FontWeight.w700 | ||||
|                                 ) | ||||
|                               ) | ||||
|                               , | ||||
|                               Text( | ||||
|                                   listInvestmentType | ||||
|                                       .map((e) => e.mutualFunds) | ||||
|                                       .reduce( | ||||
|                                           (value, element) => value + element) | ||||
|                                       .toString(), | ||||
|                                   style: const TextStyle( | ||||
|                                       fontSize: 44, | ||||
|                                       fontWeight: FontWeight.w700)), | ||||
|                             ], | ||||
|                           ) | ||||
|                         ]), | ||||
| @@ -147,9 +150,31 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|                         ), | ||||
|                         menuPortofolio(), | ||||
|                         const SizedBox( | ||||
|                           height: 24, | ||||
|                           height: 12, | ||||
|                         ), | ||||
|                         ...listColumnPortofolio(), | ||||
|                         const Padding( | ||||
|                           padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12), | ||||
|                           child: Row( | ||||
|                             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                             children: [ | ||||
|                               TextTitle(title: 'My Portfolio', fontSize: 16), | ||||
|                               Row( | ||||
|                                 children: [ | ||||
|                                   Icon(Icons.add, size: 18, color: ColorPalette.primary), | ||||
|                                   SizedBox(width: 4), | ||||
|                                   const Text('Create', | ||||
|                                     style: TextStyle( | ||||
|                                       color: ColorPalette.primary, | ||||
|                                       fontWeight: FontWeight.w600 | ||||
|                                     ), | ||||
|                                   ) | ||||
|                                 ], | ||||
|                               ) | ||||
|                             ], | ||||
|                           ), | ||||
|                         ), | ||||
|                         // ...listColumnPortofolio(), | ||||
|                         cardPortfolio() | ||||
|                       ], | ||||
|                     ), | ||||
|                   ), | ||||
| @@ -168,7 +193,7 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|       children: [ | ||||
|         Row( | ||||
|           children: [ | ||||
|             const Text('Portofolio Value', | ||||
|             const Text('Portfolio Value', | ||||
|                 style: TextStyle(color: Colors.white)), | ||||
|             const SizedBox( | ||||
|               width: 8, | ||||
| @@ -179,8 +204,12 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|                     seePortofolioValue = !seePortofolioValue; | ||||
|                   }); | ||||
|                 }, | ||||
|                 child: const Icon(Icons.visibility_outlined, | ||||
|                     color: Color(0xff93C5FD))) | ||||
|                 child: Icon( | ||||
|                     seePortofolioValue | ||||
|                         ? Icons.visibility_off_outlined | ||||
|                         : Icons.visibility_outlined, | ||||
|                     size: 18, | ||||
|                     color: const Color(0xff93C5FD))) | ||||
|           ], | ||||
|         ), | ||||
|         const SizedBox( | ||||
| @@ -197,7 +226,10 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|               firstChild: RichText( | ||||
|                   text: const TextSpan( | ||||
|                       text: 'Rp ', | ||||
|                       style: TextStyle(fontSize: 32, color: Color(0xff93C5FD), fontFamily: 'Manrope'), | ||||
|                       style: TextStyle( | ||||
|                           fontSize: 32, | ||||
|                           color: Color(0xff93C5FD), | ||||
|                           fontFamily: 'Manrope'), | ||||
|                       children: [ | ||||
|                     TextSpan( | ||||
|                       text: '22.500.000', | ||||
| @@ -205,8 +237,7 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|                           fontSize: 32, | ||||
|                           fontFamily: 'Manrope', | ||||
|                           fontWeight: FontWeight.bold, | ||||
|                           color: Colors.white | ||||
|                       ), | ||||
|                           color: Colors.white), | ||||
|                     ) | ||||
|                   ])), | ||||
|               secondChild: Padding( | ||||
| @@ -276,8 +307,8 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|                   width: 58, | ||||
|                   alignment: Alignment.center, | ||||
|                   decoration: BoxDecoration( | ||||
|                       shape: BoxShape.circle, | ||||
|                       color: bgContentColor[e.key], | ||||
|                     shape: BoxShape.circle, | ||||
|                     color: bgContentColor[e.key], | ||||
|                   ), | ||||
|                   child: Text( | ||||
|                     '${e.value.value}%', | ||||
| @@ -353,4 +384,85 @@ class _PortofolioViewState extends State<PortofolioView> { | ||||
|       ); | ||||
|     }).toList(); | ||||
|   } | ||||
|  | ||||
|   Widget cardPortfolio() { | ||||
|     return GestureDetector( | ||||
|       onTap: () { | ||||
|         // routePush(context, page: const PortfolioDetailView()); | ||||
|       }, | ||||
|       child: Container( | ||||
|         margin: const EdgeInsets.symmetric(horizontal: 24), | ||||
|         padding: const EdgeInsets.all(16), | ||||
|         decoration: BoxDecoration( | ||||
|           borderRadius: BorderRadius.circular(8), | ||||
|           border: Border.all(color: ColorPalette.slate200) | ||||
|         ), | ||||
|         child: Column( | ||||
|           children: [ | ||||
|             Row( | ||||
|               children: [ | ||||
|                 Container( | ||||
|                     padding: const EdgeInsets.all(4), | ||||
|                     decoration: BoxDecoration( | ||||
|                         color: ColorPalette.blue200.withOpacity(0.5), | ||||
|                         borderRadius: BorderRadius.circular(8) | ||||
|                     ), | ||||
|                     child: ImageView( | ||||
|                         image: PathAssets.goalInvestIcon['Education'], | ||||
|                         width: SizeConfig.width * 0.07 | ||||
|                     ) | ||||
|                 ), | ||||
|                 const SizedBox(width: 8), | ||||
|                 const Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     TextTitle(title: 'Education', fontSize: 16,), | ||||
|                     Text('2 Subscriptions', | ||||
|                       style: TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontWeight: FontWeight.w600 | ||||
|                       ), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|             const Padding( | ||||
|               padding: EdgeInsets.symmetric(vertical: 8), | ||||
|               child: Divider(height: 1, color: ColorPalette.slate200), | ||||
|             ), | ||||
|             const Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     Text('Portfolio Value', | ||||
|                       style: TextStyle( | ||||
|                           color: ColorPalette.slate400, | ||||
|                           fontWeight: FontWeight.w400 | ||||
|                       ), | ||||
|                     ), | ||||
|                     TextTitle(title: 'Rp 2.000.000', fontSize: 14,) | ||||
|                   ], | ||||
|                 ), | ||||
|                 Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.end, | ||||
|                   children: [ | ||||
|                     Text('Advantages', | ||||
|                       style: TextStyle( | ||||
|                           color: ColorPalette.slate400, | ||||
|                           fontWeight: FontWeight.w400 | ||||
|                       ), | ||||
|                     ), | ||||
|                     TextTitle(title: 'Rp 2.000.000', fontSize: 14) | ||||
|                   ], | ||||
|                 ) | ||||
|               ], | ||||
|             ) | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,171 @@ | ||||
| import 'dart:math'; | ||||
|  | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/numeric_pad/numeric_pad.dart'; | ||||
| import 'package:cims_apps/application/component/subscribe/input_investment_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/portfolio/redeem_product/view/redeem_product.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class ChangeAmount extends StatefulWidget { | ||||
|   final int totalAmount; | ||||
|   const ChangeAmount({super.key, required this.totalAmount}); | ||||
|  | ||||
|   @override | ||||
|   State<ChangeAmount> createState() => _ChangeAmountState(); | ||||
| } | ||||
|  | ||||
| class _ChangeAmountState extends State<ChangeAmount> { | ||||
|   TextEditingController amountController = TextEditingController(); | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     // TODO: implement initState | ||||
|     super.initState(); | ||||
|     amountController.text = NumberFormatter.numberCurrency(widget.totalAmount, 'Rp ', 'id_ID', decimalDigits: 0); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     // TODO: implement dispose | ||||
|     super.dispose(); | ||||
|     amountController.dispose(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => RedeemProductViewModel(), | ||||
|       child: Consumer<RedeemProductViewModel>( | ||||
|         builder: (context, provider, child) { | ||||
|           return Column( | ||||
|             mainAxisSize: MainAxisSize.min, | ||||
|             children: [ | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     GestureDetector( | ||||
|                         onTap: () { | ||||
|                           Navigator.pop(context); | ||||
|                           showModalBottomSheet( | ||||
|                             context: context, | ||||
|                             isScrollControlled: true, | ||||
|                             builder: (context) { | ||||
|                               return const RedeemProduct(); | ||||
|                             }, | ||||
|                           ); | ||||
|                         }, | ||||
|                         child: const Icon(Icons.arrow_back_rounded) | ||||
|                     ), | ||||
|                     const Text('Type amount', | ||||
|                       style: TextStyle( | ||||
|                           fontWeight: FontWeight.w600, | ||||
|                           fontSize: 16, | ||||
|                           color: ColorPalette.slate800 | ||||
|                       ), | ||||
|                     ), | ||||
|                     GestureDetector( | ||||
|                         onTap: () { | ||||
|                           Navigator.pop(context); | ||||
|                         }, | ||||
|                         child: const Icon(Icons.close_rounded) | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|               const Divider(height: 1, color: ColorPalette.slate200,), | ||||
|               Column( | ||||
|                 crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                 children: [ | ||||
|                   Padding( | ||||
|                     padding: EdgeInsets.all(24), | ||||
|                     child: cardProduct() | ||||
|                   ), | ||||
|                   InputInvestmentView( | ||||
|                     minimumPrice: (provider.getCurrentProduct.priceUnit! * 1).toInt(), | ||||
|                     maximumPrice: (provider.getCurrentProduct.priceUnit! * provider.getCurrentProduct.totalUnit!).toInt(), | ||||
|                     currentPrice: provider.getAmount!.toInt(), | ||||
|                     nextMove: (value) { | ||||
|                       String formatValueInput = value.replaceAll('Rp ', '').replaceAll('.', ''); | ||||
|                       provider.setAmount(double.parse(formatValueInput)); | ||||
|                       Navigator.pop(context); | ||||
|                       showModalBottomSheet( | ||||
|                         context: context, | ||||
|                         isScrollControlled: true, | ||||
|                         builder: (context) { | ||||
|                           return const RedeemProduct(); | ||||
|                         }, | ||||
|                       ); | ||||
|                     }, | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|               SizedBox(height: 16,) | ||||
|             ], | ||||
|           ); | ||||
|         } | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget cardProduct() { | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.all(16), | ||||
|       decoration: BoxDecoration( | ||||
|           color: Colors.white, | ||||
|           borderRadius: BorderRadius.circular(12), | ||||
|           border: Border.all(color: ColorPalette.slate200), | ||||
|           boxShadow: [ | ||||
|             BoxShadow( | ||||
|                 color: const Color(0xff1E293B).withOpacity(0.04), | ||||
|                 blurRadius: 8, | ||||
|                 spreadRadius: 2 | ||||
|             ) | ||||
|           ] | ||||
|       ), | ||||
|       child: Row( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           ImageView( | ||||
|             image: PathAssets.imgProduct, | ||||
|             width: SizeConfig.width * .13, | ||||
|           ), | ||||
|           const SizedBox(width: 8), | ||||
|           Expanded( | ||||
|             child: Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 const TextTitle(title: 'Gemilang Dana Kas Maxima', fontSize: 16,), | ||||
|                 const SizedBox(height: 4), | ||||
|                 Container( | ||||
|                   padding: const EdgeInsets.all(6), | ||||
|                   decoration: BoxDecoration( | ||||
|                       borderRadius: BorderRadius.circular(40), | ||||
|                       color: ColorPalette.investTypeBgColor['Money Market']?.withOpacity(0.5) ?? Colors.white, | ||||
|                       border: Border.all(width: 2, color: ColorPalette.investTypeColor['Money Market']?.withOpacity(0.4) ?? Colors.white) | ||||
|                   ), | ||||
|                   child: Text( | ||||
|                     'Money Market' ?? '', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.investTypeColor['Money Market'], | ||||
|                         fontWeight: FontWeight.w600 | ||||
|                     ), | ||||
|                   ), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,191 @@ | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/redeem_product.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class ChangeDestinationAccount extends StatelessWidget { | ||||
|   const ChangeDestinationAccount({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     List<Account> listAccount = [ | ||||
|       Account('Muhamad Rosyidin', 'BRI', '902139012324'), | ||||
|       Account('Achmad Muhaimin', 'BCA', '21391283928') | ||||
|     ]; | ||||
|  | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => RedeemProductViewModel(), | ||||
|       child: Consumer<RedeemProductViewModel>( | ||||
|         builder: (context, provider, child) { | ||||
|           return Column( | ||||
|             mainAxisSize: MainAxisSize.min, | ||||
|             children: [ | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     GestureDetector( | ||||
|                       onTap: () { | ||||
|                         Navigator.pop(context); | ||||
|                         showModalBottomSheet( | ||||
|                           context: context, | ||||
|                           isScrollControlled: true, | ||||
|                           builder: (context) { | ||||
|                             return const RedeemProduct(); | ||||
|                           }, | ||||
|                         ); | ||||
|                       }, | ||||
|                       child: const Icon(Icons.arrow_back_rounded) | ||||
|                     ), | ||||
|                     const Text('Change Destination Account', | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         fontSize: 16, | ||||
|                         color: ColorPalette.slate800 | ||||
|                       ), | ||||
|                     ), | ||||
|                     GestureDetector( | ||||
|                       onTap: () { | ||||
|                         Navigator.pop(context); | ||||
|                       }, | ||||
|                       child: const Icon(Icons.close_rounded) | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|               const Divider(height: 1, color: ColorPalette.slate200,), | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.all(24), | ||||
|                 child: Column( | ||||
|                   children: [ | ||||
|                     ...listAccount.asMap().entries.map((e) { | ||||
|                       return GestureDetector( | ||||
|                         onTap: () => provider.setSelectedAcc(e.value), | ||||
|                         child: Padding( | ||||
|                           padding: EdgeInsets.only(top: e.key != 0 ? 16 : 0), | ||||
|                           child: cardAccount(e.value, provider.selectedAccount?.number ?? provider.getCurrentAccount.number), | ||||
|                         ), | ||||
|                       ); | ||||
|                     }), | ||||
|                     const SizedBox(height: 24), | ||||
|                     GestureDetector( | ||||
|                       child: const Row( | ||||
|                         mainAxisSize: MainAxisSize.min, | ||||
|                         mainAxisAlignment: MainAxisAlignment.center, | ||||
|                         children: [ | ||||
|                           Icon(Icons.add, size: 24, color: ColorPalette.primary), | ||||
|                           SizedBox(width: 12,), | ||||
|                           Text('New Bank', | ||||
|                             style: TextStyle( | ||||
|                                 color: ColorPalette.primary, | ||||
|                                 fontSize: 20, | ||||
|                                 fontWeight: FontWeight.w600 | ||||
|                             ), | ||||
|                           ) | ||||
|                         ], | ||||
|                       ), | ||||
|                     ), | ||||
|                     const SizedBox(height: 32), | ||||
|                     ButtonView( | ||||
|                       name: 'Save', | ||||
|                       textSize: 20, | ||||
|                       marginVertical: 0, | ||||
|                       onPressed: () { | ||||
|                         Navigator.pop(context); | ||||
|                         showModalBottomSheet( | ||||
|                           context: context, | ||||
|                           isScrollControlled: true, | ||||
|                           builder: (context) { | ||||
|                             provider.setCurrentAcc(provider.selectedAccount!); | ||||
|                             return const RedeemProduct(); | ||||
|                           }, | ||||
|                         ); | ||||
|                       }, | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|             ], | ||||
|           ); | ||||
|         } | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget cardAccount(Account account, String numberSelected) { | ||||
|     List<String> listNumber = []; | ||||
|  | ||||
|     List.generate(account.number.length, (index) { | ||||
|       if(index > 3 && index < account.number.length - 4){ | ||||
|         listNumber.add('*'); | ||||
|       }else{ | ||||
|         listNumber.add(account.number[index]); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.all(16), | ||||
|       decoration: BoxDecoration( | ||||
|           borderRadius: BorderRadius.circular(14), | ||||
|           border: Border.all(color: ColorPalette.slate200) | ||||
|       ), | ||||
|       child: Row( | ||||
|         children: [ | ||||
|           Expanded( | ||||
|             child: Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     Text(account.nameOwner, | ||||
|                       style: const TextStyle( | ||||
|                           fontSize: 16, | ||||
|                           fontWeight: FontWeight.w600, | ||||
|                           color: ColorPalette.slate800 | ||||
|                       ), | ||||
|                     ), | ||||
|                     Text( | ||||
|                       '${account.nameBank} - ${listNumber.join("")}', | ||||
|                       style: const TextStyle( | ||||
|                           fontSize: 16, | ||||
|                           fontWeight: FontWeight.w600, | ||||
|                           color: ColorPalette.slate400 | ||||
|                       ), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ), | ||||
|           AnimatedContainer( | ||||
|             margin: const EdgeInsets.only(top: 4), | ||||
|             duration: const Duration(milliseconds: 200), | ||||
|             height: 16, | ||||
|             width: 16, | ||||
|             padding: const EdgeInsets.all(1), | ||||
|             alignment: Alignment.center, | ||||
|             decoration: BoxDecoration( | ||||
|                 shape: BoxShape.circle, | ||||
|                 border: Border.all( | ||||
|                     color: numberSelected == account.number | ||||
|                         ? ColorPalette.primary | ||||
|                         : ColorPalette.slate200)), | ||||
|             child: AnimatedContainer( | ||||
|               duration: const Duration(milliseconds: 200), | ||||
|               child: Container( | ||||
|                 decoration: BoxDecoration( | ||||
|                     color: | ||||
|                     numberSelected == account.number ? ColorPalette.primary : ColorPalette.white, | ||||
|                     shape: BoxShape.circle), | ||||
|               ), | ||||
|             ), | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,360 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/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/portfolio/redeem_product/view/change_amount.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/change_destination_account.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view/total_redeem.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class RedeemProduct extends StatefulWidget { | ||||
|   const RedeemProduct({super.key}); | ||||
|  | ||||
|   @override | ||||
|   State<RedeemProduct> createState() => _RedeemProductState(); | ||||
| } | ||||
|  | ||||
| class _RedeemProductState extends State<RedeemProduct> { | ||||
|   TextEditingController amountController = TextEditingController(); | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     // TODO: implement initState | ||||
|     super.initState(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   void dispose() { | ||||
|     // TODO: implement dispose | ||||
|     super.dispose(); | ||||
|     amountController.dispose(); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => RedeemProductViewModel(), | ||||
|       child: Consumer<RedeemProductViewModel>( | ||||
|         builder: (context, provider, child) { | ||||
|           amountController.text = NumberFormatter.numberCurrency(provider.getAmount!.toInt(), 'Rp ', 'id_ID', decimalDigits: 0); | ||||
|           return Column( | ||||
|             mainAxisSize: MainAxisSize.min, | ||||
|             children: [ | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     const Text('Products to be Redeemed', | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         color: ColorPalette.slate800, | ||||
|                         fontSize: 16 | ||||
|                       ), | ||||
|                     ), | ||||
|                     GestureDetector( | ||||
|                       onTap: () => Navigator.pop(context), | ||||
|                       child: const Icon(Icons.close_rounded, color: ColorPalette.slate800,) | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|               const Divider(height: 1, color: ColorPalette.slate200,), | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|                 child: Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   children: [ | ||||
|                     cardProduct(), | ||||
|                     const SizedBox(height: 16), | ||||
|                     segmentAmount( | ||||
|                       context, | ||||
|                       provider.getAmount!, | ||||
|                       provider.getUnit!, | ||||
|                       (value) { | ||||
|                         provider.setUnit(value); | ||||
|                       }, | ||||
|                     ), | ||||
|                     const SizedBox(height: 16), | ||||
|                     segmentDestinationAcc(provider.getCurrentAccount), | ||||
|                     const SizedBox(height: 36), | ||||
|                     ButtonView( | ||||
|                       name: 'Redeem', | ||||
|                       textSize: 20, | ||||
|                       marginVertical: 0, | ||||
|                       onPressed: () { | ||||
|                         Navigator.pop(context); | ||||
|                         showModalBottomSheet( | ||||
|                           context: context, | ||||
|                           isScrollControlled: true, | ||||
|                           builder: (context) { | ||||
|                             return TotalRedeem(); | ||||
|                           }, | ||||
|                         ); | ||||
|                       }, | ||||
|                     ), | ||||
|                     const SizedBox(height: 16) | ||||
|                   ], | ||||
|                 ), | ||||
|               ) | ||||
|             ], | ||||
|           ); | ||||
|         } | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget cardProduct() { | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.all(16), | ||||
|       decoration: BoxDecoration( | ||||
|           color: Colors.white, | ||||
|           borderRadius: BorderRadius.circular(12), | ||||
|           border: Border.all(color: ColorPalette.slate200), | ||||
|           boxShadow: [ | ||||
|             BoxShadow( | ||||
|                 color: const Color(0xff1E293B).withOpacity(0.04), | ||||
|                 blurRadius: 8, | ||||
|                 spreadRadius: 2 | ||||
|             ) | ||||
|           ] | ||||
|       ), | ||||
|       child: Row( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           ImageView( | ||||
|             image: PathAssets.imgProduct, | ||||
|             width: SizeConfig.width * .13, | ||||
|           ), | ||||
|           const SizedBox(width: 8), | ||||
|           Expanded( | ||||
|             child: Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 const TextTitle(title: 'Gemilang Dana Kas Maxima', fontSize: 16,), | ||||
|                 const SizedBox(height: 4), | ||||
|                 Container( | ||||
|                   padding: const EdgeInsets.all(6), | ||||
|                   decoration: BoxDecoration( | ||||
|                       borderRadius: BorderRadius.circular(40), | ||||
|                       color: ColorPalette.investTypeBgColor['Money Market']?.withOpacity(0.5) ?? Colors.white, | ||||
|                       border: Border.all(width: 2, color: ColorPalette.investTypeColor['Money Market']?.withOpacity(0.4) ?? Colors.white) | ||||
|                   ), | ||||
|                   child: Text( | ||||
|                     'Money Market' ?? '', | ||||
|                     style: TextStyle( | ||||
|                         color: ColorPalette.investTypeColor['Money Market'], | ||||
|                         fontWeight: FontWeight.w600 | ||||
|                     ), | ||||
|                   ), | ||||
|                 ) | ||||
|               ], | ||||
|             ), | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget segmentAmount(context, double currentAmount, double currentUnit, void Function(double value) setUnit) { | ||||
|     double sliderValue = currentUnit / Provider.of<RedeemProductViewModel>(context, listen: false).getCurrentProduct.totalUnit!; | ||||
|     return Column( | ||||
|       children: [ | ||||
|         const Align( | ||||
|           alignment: Alignment.centerLeft, | ||||
|           child: Text('Amount', | ||||
|             style: TextStyle( | ||||
|               fontWeight: FontWeight.w600, | ||||
|               color: ColorPalette.slate800, | ||||
|               fontSize: 16 | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         TextField( | ||||
|           controller: amountController, | ||||
|           textAlign: TextAlign.center, | ||||
|           style: const TextStyle( | ||||
|               fontSize: 28, | ||||
|               fontWeight: FontWeight.w700, | ||||
|               color: ColorPalette.slate800 | ||||
|           ), | ||||
|           keyboardType: TextInputType.number, | ||||
|           readOnly: true, | ||||
|           decoration: InputDecoration( | ||||
|             enabledBorder: const UnderlineInputBorder( | ||||
|               borderSide: BorderSide( | ||||
|                   color: ColorPalette.slate200, | ||||
|                   width: 1 | ||||
|               ), | ||||
|             ), | ||||
|             suffixIcon: GestureDetector( | ||||
|               onTap: () { | ||||
|                 Navigator.pop(context); | ||||
|                 showModalBottomSheet( | ||||
|                   context: context, | ||||
|                   isScrollControlled: true, | ||||
|                   builder: (context) { | ||||
|                     return ChangeAmount( | ||||
|                       totalAmount: currentAmount.toInt(), | ||||
|                     ); | ||||
|                   }, | ||||
|                 ); | ||||
|               }, | ||||
|               child: const Icon(Icons.edit, color: ColorPalette.primary), | ||||
|             ) | ||||
|           ), | ||||
|         ), | ||||
|         Padding( | ||||
|           padding: const EdgeInsets.symmetric(vertical: 20), | ||||
|           child: Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.center, | ||||
|             children: [ | ||||
|               Text(currentUnit.toStringAsFixed(2).replaceAll('.', ','), | ||||
|                 style: const TextStyle( | ||||
|                   fontWeight: FontWeight.w700, | ||||
|                   fontSize: 18, | ||||
|                   color: ColorPalette.slate800 | ||||
|                 ), | ||||
|               ), | ||||
|               const Text(' unit', | ||||
|                 style: TextStyle( | ||||
|                   fontWeight: FontWeight.w600, | ||||
|                   color: ColorPalette.slate400 | ||||
|                 ), | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|         Row( | ||||
|           children: [ | ||||
|             Expanded( | ||||
|               child: SliderTheme( | ||||
|                 data: SliderTheme.of(context).copyWith( | ||||
|                   trackHeight: 4.0, | ||||
|                   thumbColor: ColorPalette.primary, | ||||
|                   thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 10.0), | ||||
|                   overlayColor:Colors.deepPurple, | ||||
|                   inactiveTickMarkColor: ColorPalette.primary, | ||||
|                   inactiveTrackColor: ColorPalette.slate200, | ||||
|                   overlayShape: SliderComponentShape.noOverlay | ||||
|                 ), | ||||
|                 child: Slider( | ||||
|                   value: sliderValue, | ||||
|                   onChanged: setUnit, | ||||
|                   label: sliderValue.round().toString(), | ||||
|                 ), | ||||
|               ), | ||||
|             ), | ||||
|             SizedBox(width: 12), | ||||
|             Text('${(sliderValue * 100).toStringAsFixed(2)} %', | ||||
|               style: const TextStyle( | ||||
|                 color: ColorPalette.slate800, | ||||
|                 fontWeight: FontWeight.w600 | ||||
|               ), | ||||
|             ) | ||||
|           ], | ||||
|         ) | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget segmentDestinationAcc(Account account) { | ||||
|     List<String> listNumber = []; | ||||
|  | ||||
|     List.generate(account.number.length, (index) { | ||||
|       if(index > 3 && index < account.number.length - 4){ | ||||
|         listNumber.add('*'); | ||||
|       }else{ | ||||
|         listNumber.add(account.number[index]); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     return Column( | ||||
|       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|       children: [ | ||||
|         const Text('Destination Account', | ||||
|           style: TextStyle( | ||||
|               fontWeight: FontWeight.w600, | ||||
|               color: ColorPalette.slate800, | ||||
|               fontSize: 16 | ||||
|           ), | ||||
|         ), | ||||
|         const SizedBox(height: 16), | ||||
|         Container( | ||||
|           padding: const EdgeInsets.all(16), | ||||
|           decoration: BoxDecoration( | ||||
|             borderRadius: BorderRadius.circular(14), | ||||
|             border: Border.all(color: ColorPalette.slate200) | ||||
|           ), | ||||
|           child: Row( | ||||
|             mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|             children: [ | ||||
|               Expanded( | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     Column( | ||||
|                       crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                       children: [ | ||||
|                         Text(account.nameOwner, | ||||
|                           style: const TextStyle( | ||||
|                             fontSize: 16, | ||||
|                             fontWeight: FontWeight.w600, | ||||
|                             color: ColorPalette.slate800 | ||||
|                           ), | ||||
|                         ), | ||||
|                         Text('${account.nameBank} - ${listNumber.join("")}', | ||||
|                           style: const TextStyle( | ||||
|                               fontSize: 16, | ||||
|                               fontWeight: FontWeight.w600, | ||||
|                               color: ColorPalette.slate400 | ||||
|                           ), | ||||
|                         ) | ||||
|                       ], | ||||
|                     ), | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|               GestureDetector( | ||||
|                 onTap: () { | ||||
|                   Navigator.pop(context); | ||||
|                   showModalBottomSheet( | ||||
|                     context: context, | ||||
|                     isScrollControlled: true, | ||||
|                     builder: (context) => const ChangeDestinationAccount(), | ||||
|                   ); | ||||
|                 }, | ||||
|                 child: Container( | ||||
|                   padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), | ||||
|                   decoration: BoxDecoration( | ||||
|                     borderRadius: BorderRadius.circular(18), | ||||
|                     color: ColorPalette.blue200.withOpacity(0.5) | ||||
|                   ), | ||||
|                   child: const Row( | ||||
|                     children: [ | ||||
|                       Icon(Icons.change_circle_outlined, color: ColorPalette.primary, size: 20), | ||||
|                       SizedBox(width: 4), | ||||
|                       Text('Change', | ||||
|                         style: TextStyle( | ||||
|                             fontSize: 16, | ||||
|                             fontWeight: FontWeight.w600, | ||||
|                             color: ColorPalette.primary | ||||
|                         ), | ||||
|                       ) | ||||
|                     ], | ||||
|                   ), | ||||
|                 ), | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ) | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,205 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | ||||
| import 'package:cims_apps/application/component/image/image_view.dart'; | ||||
| import 'package:cims_apps/application/component/radio_agreement.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/portfolio/redeem_product/view_model/redeem_product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class TotalRedeem extends StatelessWidget { | ||||
|   const TotalRedeem({super.key}); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => RedeemProductViewModel(), | ||||
|       child: Consumer<RedeemProductViewModel>( | ||||
|         builder: (context, provider, child) { | ||||
|           return Column( | ||||
|             mainAxisSize: MainAxisSize.min, | ||||
|             children: [ | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     const Text('Investment Funds that You Cash Out', | ||||
|                       style: TextStyle( | ||||
|                           fontWeight: FontWeight.w600, | ||||
|                           color: ColorPalette.slate800, | ||||
|                           fontSize: 16 | ||||
|                       ), | ||||
|                     ), | ||||
|                     GestureDetector( | ||||
|                         onTap: () => Navigator.pop(context), | ||||
|                         child: const Icon(Icons.close_rounded, color: ColorPalette.slate800,) | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|               const Divider(height: 1, color: ColorPalette.slate200,), | ||||
|               Padding( | ||||
|                 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|                 child: cardProduct(provider.getCurrentProduct, provider.getUnit!), | ||||
|               ), | ||||
|               const Padding( | ||||
|                 padding: EdgeInsets.symmetric(horizontal: 24), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     Text('Sales Commission', | ||||
|                       style: TextStyle( | ||||
|                         color: ColorPalette.slate400, | ||||
|                         fontSize: 16 | ||||
|                       ), | ||||
|                     ), | ||||
|                     Text('Free', | ||||
|                       style: TextStyle( | ||||
|                         fontWeight: FontWeight.w600, | ||||
|                         color: ColorPalette.primary, | ||||
|                         fontSize: 16 | ||||
|                       ), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|               const SizedBox(height: 16), | ||||
|               const Padding( | ||||
|                 padding: EdgeInsets.symmetric(horizontal: 24), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     Text('Cash Out Method', | ||||
|                       style: TextStyle( | ||||
|                           color: ColorPalette.slate400, | ||||
|                           fontSize: 16 | ||||
|                       ), | ||||
|                     ), | ||||
|                     Text('Regular', | ||||
|                       style: TextStyle( | ||||
|                           fontWeight: FontWeight.w600, | ||||
|                           color: ColorPalette.primary, | ||||
|                           fontSize: 16 | ||||
|                       ), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|               const SizedBox(height: 16,), | ||||
|               Container( | ||||
|                 padding: EdgeInsets.symmetric(horizontal: 24, vertical: 16), | ||||
|                 color: ColorPalette.slate200.withOpacity(0.5), | ||||
|                 child: Row( | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     Expanded( | ||||
|                       child: TextTitle( | ||||
|                         title: 'Estimated Funds Disbursed', | ||||
|                         color: ColorPalette.slate500, | ||||
|                         fontSize: 16, | ||||
|                       ) | ||||
|                     ), | ||||
|                     Expanded( | ||||
|                       child: Text( | ||||
|                         NumberFormatter.numberCurrency((provider.getCurrentProduct.priceUnit! * provider.getUnit!).toInt(), 'Rp ', 'id_ID', decimalDigits: 0), | ||||
|                         textAlign: TextAlign.end, | ||||
|                         style: TextStyle( | ||||
|                           color: ColorPalette.slate800, | ||||
|                           fontWeight: FontWeight.w700, | ||||
|                           fontSize: 18 | ||||
|                         ), | ||||
|                       ) | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ), | ||||
|               RadioAgreement( | ||||
|                 isAgree: provider.isAgree, | ||||
|                 desc: 'I agree to the sale of the mutual funds listed on this page. I understand that the total funds disbursed are approximate. The amount received may change according to the closing price of the mutual fund on the day of sale.', | ||||
|                 onTap: provider.setAgree | ||||
|               ), | ||||
|               const SizedBox(height: 24), | ||||
|               ButtonView( | ||||
|                 disabled: !provider.isAgree, | ||||
|                 name: 'Redeem', | ||||
|                 textSize: 20, | ||||
|                 marginVertical: 0, | ||||
|                 disabledBgColor: ColorPalette.slate200.withOpacity(0.5), | ||||
|                 textColor: !provider.isAgree ? ColorPalette.slate400 : Colors.white, | ||||
|                 onPressed: () { | ||||
|                   Navigator.pop(context); | ||||
|                   showModalBottomSheet( | ||||
|                     context: context, | ||||
|                     isScrollControlled: true, | ||||
|                     builder: (context) { | ||||
|                       return TotalRedeem(); | ||||
|                     }, | ||||
|                   ); | ||||
|                 }, | ||||
|               ), | ||||
|               const SizedBox(height: 24) | ||||
|             ], | ||||
|           ); | ||||
|         } | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget cardProduct(PortfolioProduct product, double currentUnit) { | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.all(16), | ||||
|       decoration: BoxDecoration( | ||||
|           color: Colors.white, | ||||
|           borderRadius: BorderRadius.circular(12), | ||||
|           border: Border.all(color: ColorPalette.slate200), | ||||
|           boxShadow: [ | ||||
|             BoxShadow( | ||||
|                 color: const Color(0xff1E293B).withOpacity(0.04), | ||||
|                 blurRadius: 8, | ||||
|                 spreadRadius: 2 | ||||
|             ) | ||||
|           ] | ||||
|       ), | ||||
|       child: Row( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           ImageView( | ||||
|             image: PathAssets.imgProduct, | ||||
|             width: SizeConfig.width * .13, | ||||
|           ), | ||||
|           const SizedBox(width: 8), | ||||
|           Expanded( | ||||
|             child: Column( | ||||
|               crossAxisAlignment: CrossAxisAlignment.start, | ||||
|               children: [ | ||||
|                 TextTitle(title: product.name ?? '', fontSize: 16,), | ||||
|                 const SizedBox(height: 4), | ||||
|                 Row( | ||||
|                   children: [ | ||||
|                     Text(currentUnit.toStringAsFixed(2).replaceAll('.', ','), | ||||
|                       style: const TextStyle( | ||||
|                           fontWeight: FontWeight.w700, | ||||
|                           fontSize: 18, | ||||
|                           color: ColorPalette.slate800 | ||||
|                       ), | ||||
|                     ), | ||||
|                     const Text(' unit', | ||||
|                       style: TextStyle( | ||||
|                           fontWeight: FontWeight.w600, | ||||
|                           color: ColorPalette.slate400 | ||||
|                       ), | ||||
|                     ) | ||||
|                   ], | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,73 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class Account { | ||||
|   String nameOwner, nameBank, number; | ||||
|  | ||||
|   Account(this.nameOwner, this.nameBank, this.number); | ||||
| } | ||||
|  | ||||
| class PortfolioProduct { | ||||
|   String? name, type; | ||||
|   double? yield; | ||||
|   double? priceUnit, funds, totalPercent, totalUnit; | ||||
|  | ||||
|   PortfolioProduct({this.name, this.type, this.yield, this.priceUnit, this.funds, this.totalPercent = 1, this.totalUnit}); | ||||
| } | ||||
|  | ||||
| class RedeemProductViewModel extends ChangeNotifier { | ||||
|   static Account currentAccount = Account('Muhamad Rosyidin', 'BRI', '902139012324'); | ||||
|   static PortfolioProduct currentProduct = | ||||
|     PortfolioProduct( | ||||
|       name: 'Gemilang Dana Kas Maxima', | ||||
|       type: '', | ||||
|       yield: 8.17, | ||||
|       priceUnit: 2600.79, | ||||
|       funds: 6300000, | ||||
|       totalUnit: 25 | ||||
|   ); | ||||
|  | ||||
|   static double? amount; | ||||
|   static double? unit; | ||||
|   Account? selectedAccount; | ||||
|  | ||||
|   Account get getCurrentAccount => currentAccount; | ||||
|   PortfolioProduct get getCurrentProduct => currentProduct; | ||||
|   double? get getAmount => amount; | ||||
|   double? get getUnit => unit; | ||||
|  | ||||
|   bool isAgree = false; | ||||
|  | ||||
|   void setCurrentAcc(Account account) { | ||||
|     currentAccount = account; | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   void setSelectedAcc(Account account){ | ||||
|     selectedAccount = account; | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   void setAmount(double amount) { | ||||
|     amount = amount; | ||||
|     unit = (amount / currentProduct.priceUnit!); | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   void setUnit(double value){ | ||||
|     unit = currentProduct.totalUnit! * value; | ||||
|     amount = unit! * currentProduct.priceUnit!; | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   void setProduct(PortfolioProduct product) { | ||||
|     currentProduct = product; | ||||
|     amount = product.priceUnit! * (product.totalUnit! / 2.0); | ||||
|     unit = (product.totalUnit! / 2.0); | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   void setAgree() { | ||||
|     isAgree = !isAgree; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| } | ||||
| @@ -13,6 +13,7 @@ import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view/step_subscribe/select_goal_investing.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter/services.dart'; | ||||
| import 'package:group_button/group_button.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| @@ -24,8 +25,9 @@ class Time { | ||||
| } | ||||
|  | ||||
| class ProductView extends StatefulWidget { | ||||
|   final String investType; | ||||
|   const ProductView(this.investType, {super.key}); | ||||
|   final bool seeMore; | ||||
|   final Product selectedProduct; | ||||
|   const ProductView({super.key, required this.selectedProduct, this.seeMore = false}); | ||||
|  | ||||
|   @override | ||||
|   State<ProductView> createState() => _ProductViewState(); | ||||
| @@ -78,6 +80,8 @@ class _ProductViewState extends State<ProductView> { | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   double currentScroll = 0; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     machineController.text = NumberFormatter.numberCurrency(100000, 'Rp ', 'id_ID', decimalDigits: 0); | ||||
| @@ -103,48 +107,91 @@ class _ProductViewState extends State<ProductView> { | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => ProductViewModel(), | ||||
|       child: Scaffold( | ||||
|         body: SizedBox( | ||||
|           child: Stack( | ||||
|             children: [ | ||||
|               const ImageView(image: PathAssets.imgDashboardAccount), | ||||
|               Column( | ||||
|                 children: [ | ||||
|                   const SizedBox( | ||||
|                     height: 50, | ||||
|                   ), | ||||
|                   const Padding( | ||||
|                     padding: EdgeInsets.symmetric(horizontal: 24), | ||||
|                     child: Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         BackButtonView(), | ||||
|                         Wrap( | ||||
|                           spacing: 12, | ||||
|                           children: [ | ||||
|                             Icon(Icons.search_rounded, color: ColorPalette.blue200, size: 32), | ||||
|                             Icon(Icons.file_download_outlined, color: ColorPalette.blue200, size: 32), | ||||
|                             Icon(Icons.star_outline_rounded, color: ColorPalette.blue200, size: 32) | ||||
|                           ], | ||||
|                         ) | ||||
|                       ], | ||||
|         body: NotificationListener<ScrollNotification>( | ||||
|           onNotification: (notification) { | ||||
|             setState(() { | ||||
|               currentScroll = notification.metrics.pixels; | ||||
|             }); | ||||
|             return false; | ||||
|           }, | ||||
|           child: CustomScrollView( | ||||
|             slivers: [ | ||||
|               SliverAppBar( | ||||
|                 toolbarHeight: 70, | ||||
|                 expandedHeight: 170, | ||||
|                 leadingWidth: 0, | ||||
|                 floating: false, | ||||
|                 automaticallyImplyLeading: false, | ||||
|                 title: AnimatedCrossFade( | ||||
|                   firstChild:  Text( | ||||
|                     widget.selectedProduct.name ?? '', | ||||
|                     maxLines: 2, | ||||
|                     overflow: TextOverflow.ellipsis, | ||||
|                     style: TextStyle( | ||||
|                         color: Colors.white, | ||||
|                         fontWeight: FontWeight.w700, | ||||
|                         fontSize: 18 | ||||
|                     ), | ||||
|                   ), | ||||
|                   const SizedBox( | ||||
|                     height: 24, | ||||
|                   secondChild: Padding( | ||||
|                     padding: const EdgeInsets.only(left: 8), | ||||
|                     child: BackButtonView(), | ||||
|                   ), | ||||
|                   headContainer(), | ||||
|                   const SizedBox( | ||||
|                     height: 24, | ||||
|                   crossFadeState: currentScroll >= 85 ? CrossFadeState.showFirst : CrossFadeState.showSecond, | ||||
|                   duration: Duration(milliseconds: 200) | ||||
|                 ), | ||||
|                 centerTitle: false, | ||||
|                 snap: false, | ||||
|                 pinned: true, | ||||
|                 backgroundColor: Color(0xFF1745C8).withOpacity(currentScroll >= 95 ? 1 : currentScroll / 95), | ||||
|                 surfaceTintColor: ColorPalette.primary, | ||||
|                 actions: [ | ||||
|                   Wrap( | ||||
|                     spacing: 12, | ||||
|                     children: [ | ||||
|                       Icon(Icons.search_rounded, color: ColorPalette.blue200, size: 32), | ||||
|                       Icon(Icons.file_download_outlined, color: ColorPalette.blue200, size: 32), | ||||
|                       Icon(Icons.star_outline_rounded, color: ColorPalette.blue200, size: 32) | ||||
|                     ], | ||||
|                   ), | ||||
|                   Expanded( | ||||
|                     child: contentContainer() | ||||
|                   ) | ||||
|                   SizedBox(width: 24,) | ||||
|                 ], | ||||
|                 flexibleSpace: Opacity( | ||||
|                   opacity: currentScroll > 0 ? ((95 - currentScroll) / 95) > 0.01 ? ((95 - currentScroll) / 95) : 0 : 1, | ||||
|                   child: Stack( | ||||
|                     children: [ | ||||
|                       ImageView(image: PathAssets.imgDashboardAccount, width: SizeConfig.width, height: 200), | ||||
|                       Column( | ||||
|                         mainAxisAlignment: MainAxisAlignment.end, | ||||
|                         children: [ | ||||
|                           headContainer(), | ||||
|                           const SizedBox( | ||||
|                             height: 24, | ||||
|                           ), | ||||
|                           Container( | ||||
|                             height: 12, | ||||
|                             decoration: BoxDecoration( | ||||
|                                 color: Colors.white, | ||||
|                                 borderRadius: BorderRadius.only( | ||||
|                                     topLeft: Radius.circular(12), | ||||
|                                     topRight: Radius.circular(12) | ||||
|                                 ) | ||||
|                             ), | ||||
|                           ) | ||||
|                         ], | ||||
|                       ) | ||||
|                     ], | ||||
|                   ), | ||||
|                 ), | ||||
|               ), | ||||
|               SliverToBoxAdapter( | ||||
|                 child: contentContainer(), | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|         bottomNavigationBar: Container( | ||||
|         bottomNavigationBar: !widget.seeMore ? | ||||
|         Container( | ||||
|           height: SizeConfig.height * .1, | ||||
|           padding: const EdgeInsets.symmetric(horizontal: 24), | ||||
|           child: ButtonView( | ||||
| @@ -159,7 +206,7 @@ class _ProductViewState extends State<ProductView> { | ||||
|             height: SizeConfig.height * 0.06, | ||||
|             marginVertical: 16, | ||||
|           ), | ||||
|         ), | ||||
|         ) : SizedBox(), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| @@ -177,7 +224,7 @@ class _ProductViewState extends State<ProductView> { | ||||
|                   SizedBox(width: 8), | ||||
|                   Expanded( | ||||
|                     child: Text( | ||||
|                       'Gemilang Dana Kas Maxima', | ||||
|                       widget.selectedProduct.name ?? '', | ||||
|                       maxLines: 2, | ||||
|                       overflow: TextOverflow.ellipsis, | ||||
|                       style: TextStyle( | ||||
| @@ -190,17 +237,18 @@ class _ProductViewState extends State<ProductView> { | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|             SizedBox(width: 12), | ||||
|             Container( | ||||
|               padding: const EdgeInsets.all(6), | ||||
|               decoration: BoxDecoration( | ||||
|                   borderRadius: BorderRadius.circular(40), | ||||
|                   color: ColorPalette.investTypeBgColor[widget.investType] ?? Colors.white, | ||||
|                   border: Border.all(width: 2, color: ColorPalette.investTypeColor[widget.investType] ?? Colors.white) | ||||
|                   color: ColorPalette.investTypeBgColor[widget.selectedProduct.type] ?? Colors.white, | ||||
|                   border: Border.all(width: 2, color: ColorPalette.investTypeColor[widget.selectedProduct.type] ?? Colors.white) | ||||
|               ), | ||||
|               child: Text( | ||||
|                 widget.investType, | ||||
|                 widget.selectedProduct.type ?? '', | ||||
|                 style: TextStyle( | ||||
|                     color: ColorPalette.investTypeColor[widget.investType], | ||||
|                     color: ColorPalette.investTypeColor[widget.selectedProduct.type], | ||||
|                     fontWeight: FontWeight.w600 | ||||
|                 ), | ||||
|               ), | ||||
| @@ -217,8 +265,11 @@ class _ProductViewState extends State<ProductView> { | ||||
|         decoration: BoxDecoration( | ||||
|           color: Colors.white, | ||||
|         ), | ||||
|         child: ListView( | ||||
|         child: Column( | ||||
|           children: [ | ||||
|             const SizedBox( | ||||
|               height: 12, | ||||
|             ), | ||||
|             ProductChartView( | ||||
|               tabType: listTab[selectedTab], | ||||
|               lastTime: selectedTime.completeName, | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import 'package:cims_apps/application/component/subscribe/goal_investing_view.da | ||||
| import 'package:cims_apps/application/component/subscribe/input_investment_view.dart'; | ||||
| import 'package:cims_apps/application/component/subscribe/total_payment_view.dart'; | ||||
| import 'package:cims_apps/application/theme/color_palette.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/plan/view_model/plan_view_model.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_account/view/product/view_model/product_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
| @@ -11,10 +12,9 @@ class SelectGoalInvesting extends StatelessWidget { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return MultiProvider( | ||||
|       providers: [ | ||||
|         ChangeNotifierProvider(create: (context) => ProductViewModel(),) | ||||
|       ], | ||||
|  | ||||
|     return ChangeNotifierProvider( | ||||
|       create: (context) => ProductViewModel(), | ||||
|       child: Consumer<ProductViewModel>( | ||||
|         builder: (context, provider, child) { | ||||
|           return Container( | ||||
| @@ -51,23 +51,50 @@ class SelectGoalInvesting extends StatelessWidget { | ||||
|                       context: context, | ||||
|                       isScrollControlled: true, | ||||
|                       builder: (context) { | ||||
|                         return InputInvestmentView( | ||||
|                           selectedPlan: p0, | ||||
|                           nextMove: (value) { | ||||
|                             Navigator.pop(context); | ||||
|                             int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll(',', '')); | ||||
|                             showModalBottomSheet( | ||||
|                               context: context, | ||||
|                               isScrollControlled: true, | ||||
|                               builder: (context) => | ||||
|                                 TotalPaymentView( | ||||
|                                   listProduct: [ | ||||
|                                     provider.getSelectedProduct | ||||
|                                   ], | ||||
|                                   totalInvest: formatIntParse, | ||||
|                                 ) | ||||
|                             ); | ||||
|                           }, | ||||
|                         return ChangeNotifierProvider( | ||||
|                           create: (context) => ProductViewModel(), | ||||
|                           child: Consumer<ProductViewModel>( | ||||
|                             builder: (context, provider, child) { | ||||
|                               return Padding( | ||||
|                                 padding: EdgeInsets.symmetric(vertical: 16), | ||||
|                                 child: InputInvestmentView( | ||||
|                                   currentPlan: p0, | ||||
|                                   changePlan: () { | ||||
|                                     Navigator.pop(context); | ||||
|                                     showModalBottomSheet( | ||||
|                                       context: context, | ||||
|                                       isScrollControlled: true, | ||||
|                                       builder: (context) => SelectGoalInvesting(), | ||||
|                                     ); | ||||
|                                   }, | ||||
|                                   nextMove: (value) { | ||||
|                                     Navigator.pop(context); | ||||
|                                     int formatIntParse = int.parse(value.replaceAll('Rp ', '').replaceAll('.', '')); | ||||
|                                     showModalBottomSheet( | ||||
|                                       context: context, | ||||
|                                       isScrollControlled: true, | ||||
|                                       builder: (context) => | ||||
|                                         ChangeNotifierProvider( | ||||
|                                           create: (context) => ProductViewModel(), | ||||
|                                           child: Consumer<ProductViewModel>( | ||||
|                                               builder: (context, provider, child) { | ||||
|                                                 return TotalPaymentView( | ||||
|                                                   listProduct: [ | ||||
|                                                     provider.getSelectedProduct | ||||
|                                                   ], | ||||
|                                                   totalInvest: formatIntParse, | ||||
|                                                   isAgree: provider.isAgree, | ||||
|                                                   onTapAgree: provider.setAgree, | ||||
|                                                 ); | ||||
|                                               } | ||||
|                                             ), | ||||
|                                           ) | ||||
|                                     ); | ||||
|                                   }, | ||||
|                                 ), | ||||
|                               ); | ||||
|                             } | ||||
|                           ), | ||||
|                         ); | ||||
|                       }, | ||||
|                     ); | ||||
|   | ||||
| @@ -14,6 +14,7 @@ class ProductViewModel extends ChangeNotifier { | ||||
|   Product get getSelectedProduct => selectedProduct; | ||||
|  | ||||
|   double totalInvestment = 0; | ||||
|   bool isAgree = false; | ||||
|  | ||||
|   void setSelectedProduct(Product product) { | ||||
|     selectedProduct = product; | ||||
| @@ -24,4 +25,9 @@ class ProductViewModel extends ChangeNotifier { | ||||
|     totalInvestment = value; | ||||
|     notifyListeners(); | ||||
|   } | ||||
|  | ||||
|   void setAgree() { | ||||
|     isAgree = !isAgree; | ||||
|     notifyListeners(); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,165 @@ | ||||
| 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:cims_apps/features/dashboard/dashboard_account/view/profile/view_model/profile_view_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class RouteNavigation { | ||||
|   String icon; | ||||
|   String title; | ||||
|   Widget destination; | ||||
|  | ||||
|   RouteNavigation(this.icon, this.title, this.destination); | ||||
| } | ||||
|  | ||||
| class ProfileView extends StatelessWidget { | ||||
|   const ProfileView({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     List<RouteNavigation> listGeneral = [ | ||||
|       RouteNavigation(PathAssets.iconProfile, 'Personal Data', Container()), | ||||
|       RouteNavigation(PathAssets.iconPadlock, 'Change Password', Container()), | ||||
|       RouteNavigation(PathAssets.iconCard, 'Add Card', Container()), | ||||
|       RouteNavigation(PathAssets.iconSetting, 'Settings', Container()) | ||||
|     ]; | ||||
|  | ||||
|     List<RouteNavigation> listPreferences = [ | ||||
|       RouteNavigation(PathAssets.iconFaqs, 'FAQs', Container()), | ||||
|       RouteNavigation(PathAssets.iconLogout, 'Log Out', Container()) | ||||
|     ]; | ||||
|  | ||||
|     return Provider( | ||||
|       create: (context) => ProfileViewModel(), | ||||
|       child: Scaffold( | ||||
|         body: SizedBox( | ||||
|           width: SizeConfig.width, | ||||
|           height: SizeConfig.height, | ||||
|           child: Stack( | ||||
|             children: [ | ||||
|               const Positioned( | ||||
|                 left: 0, | ||||
|                 right: 0, | ||||
|                 top: 0, | ||||
|                 bottom: 0, | ||||
|                 child: ImageView(image: PathAssets.imgDashboardProfile) | ||||
|               ), | ||||
|               Consumer<ProfileViewModel>( | ||||
|                 builder: (context, provider, child) { | ||||
|                   return ListView( | ||||
|                     padding: const EdgeInsets.all(0), | ||||
|                     children: [ | ||||
|                       const SizedBox( | ||||
|                         height: 50, | ||||
|                       ), | ||||
|                       const Center( | ||||
|                         child: TextTitle(title: 'Profile', color: Colors.white, fontSize: 20,) | ||||
|                       ), | ||||
|                       SizedBox( | ||||
|                         height: SizeConfig.height * 0.05, | ||||
|                       ), | ||||
|                       Container( | ||||
|                         alignment: Alignment.center, | ||||
|                         decoration: BoxDecoration( | ||||
|                           shape: BoxShape.circle, | ||||
|                           color: Colors.white, | ||||
|                           border: Border.all(color: ColorPalette.slate200, width: 1.5) | ||||
|                         ), | ||||
|                         padding: const EdgeInsets.all(8), | ||||
|                         child: ImageView(image: PathAssets.iconCat, width: SizeConfig.width * 0.14,), | ||||
|                       ), | ||||
|                       const SizedBox(height: 16), | ||||
|                       Center( | ||||
|                         child: TextTitle(title: provider.getUser.name ?? '', color: Colors.white, fontSize: 24,)), | ||||
|                       Text('Investor ${provider.getUser.risk}', | ||||
|                         textAlign: TextAlign.center, | ||||
|                         style: TextStyle( | ||||
|                           fontSize: 16, | ||||
|                           color: ColorPalette.textRiskColor[provider.getUser.risk] | ||||
|                         ), | ||||
|                       ), | ||||
|                       const SizedBox(height: 24), | ||||
|                       boxNavigation('General', listGeneral), | ||||
|                       const SizedBox(height: 24), | ||||
|                       boxNavigation('Preferences', listPreferences) | ||||
|                     ], | ||||
|                   ); | ||||
|                 } | ||||
|               ) | ||||
|             ], | ||||
|           ), | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget boxNavigation(String title, List<RouteNavigation> list) { | ||||
|     return Container( | ||||
|       padding: const EdgeInsets.all(16), | ||||
|       margin: const EdgeInsets.symmetric(horizontal: 24), | ||||
|       decoration: BoxDecoration( | ||||
|         color: Colors.white, | ||||
|         borderRadius: BorderRadius.circular(12), | ||||
|       ), | ||||
|       child: Column( | ||||
|         crossAxisAlignment: CrossAxisAlignment.start, | ||||
|         children: [ | ||||
|           Text(title, | ||||
|             style: const TextStyle( | ||||
|                 fontWeight: FontWeight.w700, | ||||
|                 color: ColorPalette.slate400 | ||||
|             ), | ||||
|           ), | ||||
|           const SizedBox(height: 16), | ||||
|           Wrap( | ||||
|             runSpacing: 16, | ||||
|             children: list.map((e) { | ||||
|               return rowNavigation( | ||||
|                   e.icon, | ||||
|                   e.title, | ||||
|                   e.destination | ||||
|               ); | ||||
|             }).toList(), | ||||
|           ) | ||||
|         ], | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Widget rowNavigation(String icon, String text, Widget destination) { | ||||
|     bool isLogout = text == 'Log Out'; | ||||
|     Color textColor = isLogout ? ColorPalette.red600 : ColorPalette.slate800; | ||||
|     Color bgLeadingColor = isLogout ? ColorPalette.red50 : ColorPalette.blue50; | ||||
|     Color iconColor = isLogout ? ColorPalette.red600 : ColorPalette.blue600; | ||||
|  | ||||
|     return Row( | ||||
|       children: [ | ||||
|         Container( | ||||
|           alignment: Alignment.center, | ||||
|           padding: const EdgeInsets.all(4), | ||||
|           decoration: BoxDecoration( | ||||
|             shape: BoxShape.circle, | ||||
|             color: bgLeadingColor | ||||
|           ), | ||||
|           child: Image.asset(icon, width: SizeConfig.width * 0.05, color: iconColor) | ||||
|         ), | ||||
|         Expanded( | ||||
|           child: Padding( | ||||
|             padding: const EdgeInsets.symmetric(horizontal: 16), | ||||
|             child: Text(text, | ||||
|               style: TextStyle( | ||||
|                 fontWeight: FontWeight.w600, | ||||
|                 fontSize: 16, | ||||
|                 color: textColor | ||||
|               ), | ||||
|             ), | ||||
|           ), | ||||
|         ), | ||||
|         const Icon(Icons.chevron_right_outlined, size: 28, color: ColorPalette.slate400) | ||||
|       ], | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,11 @@ | ||||
| class User { | ||||
|   String? name, risk; | ||||
|  | ||||
|   User({this.name, this.risk}); | ||||
| } | ||||
|  | ||||
| class ProfileViewModel { | ||||
|   static final User _user = User(name: 'Muhammad Rosyidin', risk: 'Conservative'); | ||||
|  | ||||
|   User get getUser => _user; | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| class LoginGmailModel { | ||||
|   String idToken; | ||||
|   String accessToken; | ||||
|  | ||||
|   LoginGmailModel({ | ||||
|     required this.idToken, | ||||
|     required this.accessToken, | ||||
|   }); | ||||
|  | ||||
|   Map<String, String> toJson() => { | ||||
|         "idToken": idToken, | ||||
|         "accessToken": accessToken, | ||||
|       }; | ||||
| } | ||||
| @@ -5,9 +5,10 @@ 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/login/view/login_view.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/initial_registration_step.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/registration_view.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_public/viewmodel/dashboard_public_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class DashboardPublicView extends StatelessWidget { | ||||
|   static const routeName = '/DashboardPublicView'; | ||||
| @@ -37,88 +38,97 @@ class DashboardPublicView extends StatelessWidget { | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return Scaffold( | ||||
|       body: SingleChildScrollView( | ||||
|         padding: const EdgeInsets.only( | ||||
|           top: 32.0, | ||||
|           bottom: 8.0, | ||||
|           left: 24.0, | ||||
|           right: 24.0, | ||||
|         ), | ||||
|         child: Column( | ||||
|           crossAxisAlignment: CrossAxisAlignment.start, | ||||
|           mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|           children: [ | ||||
|             ImageView( | ||||
|               image: PathAssets.icon1, | ||||
|               width: SizeConfig.width * .35, | ||||
|             ), | ||||
|             Align( | ||||
|                 alignment: Alignment.center, | ||||
|                 heightFactor: 1, | ||||
|                 child: _caption()), | ||||
|             Align( | ||||
|               alignment: Alignment.center, | ||||
|               child: ImageView( | ||||
|                 image: PathAssets.imgDashboard, | ||||
|                 width: SizeConfig.width * .7, | ||||
|     return ChangeNotifierProvider( | ||||
|         create: (context) => DashboardPublicViewModel(), | ||||
|         builder: (context, child) { | ||||
|           return Scaffold( | ||||
|             body: SingleChildScrollView( | ||||
|               padding: const EdgeInsets.only( | ||||
|                 bottom: 24.0, | ||||
|                 left: 24.0, | ||||
|                 right: 24.0, | ||||
|               ), | ||||
|               child: Consumer<DashboardPublicViewModel>( | ||||
|                   builder: (context, provider, child) { | ||||
|                 return Column( | ||||
|                   crossAxisAlignment: CrossAxisAlignment.start, | ||||
|                   mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                   children: [ | ||||
|                     SizedBox(height: SizeConfig.height * .06), | ||||
|                     ImageView( | ||||
|                       image: PathAssets.icon1, | ||||
|                       width: SizeConfig.width * .35, | ||||
|                     ), | ||||
|                     SizedBox(height: SizeConfig.height * .02), | ||||
|                     Align( | ||||
|                         alignment: Alignment.center, | ||||
|                         heightFactor: 1, | ||||
|                         child: _caption()), | ||||
|                     Align( | ||||
|                       alignment: Alignment.center, | ||||
|                       child: ImageView( | ||||
|                         image: PathAssets.imgDashboard, | ||||
|                         width: SizeConfig.width * .8, | ||||
|                       ), | ||||
|                     ), | ||||
|                     Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         ButtonView( | ||||
|                           name: 'Sign in', | ||||
|                           isOutlined: true, | ||||
|                           width: SizeConfig.width * .43, | ||||
|                           height: SizeConfig.height * .06, | ||||
|                           onPressed: () { | ||||
|                             routePush(context, page: const LoginView()); | ||||
|                           }, | ||||
|                         ), | ||||
|                         ButtonView( | ||||
|                           name: 'Sign Up', | ||||
|                           width: SizeConfig.width * .43, | ||||
|                           height: SizeConfig.height * .06, | ||||
|                           onPressed: () { | ||||
|                             routePush(context, page: const RegistrationView()); | ||||
|                           }, | ||||
|                         ), | ||||
|                       ], | ||||
|                     ), | ||||
|                     const ImageView(image: PathAssets.iconConnect), | ||||
|                     ButtonView( | ||||
|                       name: 'Google', | ||||
|                       isSecondaryColor: true, | ||||
|                       isOutlined: true, | ||||
|                       prefixIcon: const ImageView( | ||||
|                         image: PathAssets.iconGoogle, | ||||
|                         width: 26, | ||||
|                       ), | ||||
|                       onPressed: () { | ||||
|                         provider.loginGoogle(context); | ||||
|                       }, | ||||
|                     ), | ||||
|                     SizedBox(height: SizeConfig.height * .07), | ||||
|                     Row( | ||||
|                       mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|                       children: [ | ||||
|                         ImageView( | ||||
|                           image: PathAssets.iconOjk, | ||||
|                           width: SizeConfig.width * .20, | ||||
|                         ), | ||||
|                         ImageView( | ||||
|                           image: PathAssets.iconInklusi, | ||||
|                           width: SizeConfig.width * .20, | ||||
|                         ), | ||||
|                         ImageView( | ||||
|                           image: PathAssets.iconReksadana, | ||||
|                           width: SizeConfig.width * .20, | ||||
|                         ), | ||||
|                       ], | ||||
|                     ) | ||||
|                   ], | ||||
|                 ); | ||||
|               }), | ||||
|             ), | ||||
|             Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 ButtonView( | ||||
|                   name: 'Sign in', | ||||
|                   isOutlined: true, | ||||
|                   width: SizeConfig.width * .43, | ||||
|                   height: SizeConfig.height * .06, | ||||
|                   onPressed: () { | ||||
|                     routePush(context, page: const LoginView()); | ||||
|                   }, | ||||
|                 ), | ||||
|                 ButtonView( | ||||
|                   name: 'Sign Up', | ||||
|                   width: SizeConfig.width * .43, | ||||
|                   height: SizeConfig.height * .06, | ||||
|                   onPressed: () { | ||||
|                     routePush(context, page: const RegistrationView()); | ||||
|                   }, | ||||
|                 ), | ||||
|               ], | ||||
|             ), | ||||
|             const ImageView(image: PathAssets.iconConnect), | ||||
|             ButtonView( | ||||
|               name: 'Google', | ||||
|               isSecondaryColor: true, | ||||
|               isOutlined: true, | ||||
|               prefixIcon: const ImageView( | ||||
|                 image: PathAssets.iconGoogle, | ||||
|                 width: 26, | ||||
|               ), | ||||
|               onPressed: () { | ||||
|                 routePush(context, page: const InitialRegistrationStep()); | ||||
|               }, | ||||
|             ), | ||||
|             Row( | ||||
|               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||
|               children: [ | ||||
|                 ImageView( | ||||
|                   image: PathAssets.iconOjk, | ||||
|                   width: SizeConfig.width * .20, | ||||
|                 ), | ||||
|                 ImageView( | ||||
|                   image: PathAssets.iconInklusi, | ||||
|                   width: SizeConfig.width * .20, | ||||
|                 ), | ||||
|                 ImageView( | ||||
|                   image: PathAssets.iconReksadana, | ||||
|                   width: SizeConfig.width * .20, | ||||
|                 ), | ||||
|               ], | ||||
|             ) | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|           ); | ||||
|         }); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,50 @@ | ||||
| import 'package:cims_apps/core/route/route.dart'; | ||||
| import 'package:cims_apps/features/auth/registration/view/initial_registration_step.dart'; | ||||
| import 'package:cims_apps/features/dashboard/dashboard_public/model/login_gmail_model.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:google_sign_in/google_sign_in.dart'; | ||||
|  | ||||
| class DashboardPublicViewModel extends ChangeNotifier { | ||||
|   String emailGoogle = ''; | ||||
|   final GoogleSignIn googleSignIn = GoogleSignIn( | ||||
|     scopes: [ | ||||
|       'email', | ||||
|       'https://www.googleapis.com/auth/contacts.readonly', | ||||
|     ], | ||||
|   ); | ||||
|   Future<LoginGmailModel?> _getGmail() async { | ||||
|     LoginGmailModel? loginGmailModel; | ||||
|     try { | ||||
|       final signInResult = await googleSignIn.signIn(); | ||||
|       if (signInResult != null) { | ||||
|         emailGoogle = signInResult.email; | ||||
|         final signInAuth = await signInResult.authentication; | ||||
|         final accessToken = signInAuth.accessToken; | ||||
|         final idToken = signInAuth.idToken; | ||||
|         if (idToken != null && accessToken != null) { | ||||
|           loginGmailModel = LoginGmailModel( | ||||
|             accessToken: accessToken, | ||||
|             idToken: idToken, | ||||
|           ); | ||||
|         } | ||||
|       } | ||||
|     } catch (e) { | ||||
|       debugPrint('catch error $e'); | ||||
|     } | ||||
|     return loginGmailModel; | ||||
|   } | ||||
|  | ||||
|   Future<bool> loginGoogle(BuildContext context) async { | ||||
|     bool loginSuccess = false; | ||||
|     loginSuccess = await _getGmail().then((payload) async { | ||||
|       bool result = false; | ||||
|       if (payload != null) { | ||||
|         debugPrint('objectzz ${payload.toJson()}'); | ||||
|         routePush(context, page: InitialRegistrationStep()); | ||||
|         googleSignIn.disconnect(); | ||||
|       } | ||||
|       return result; | ||||
|     }); | ||||
|     return loginSuccess; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										35
									
								
								lib/features/transaction/view/cancel_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,35 @@ | ||||
| import 'package:cims_apps/application/component/card_transaction/card_transaction_view.dart'; | ||||
| import 'package:cims_apps/application/component/card_transaction/empty_card_transaction.dart'; | ||||
| import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class CancelView extends StatelessWidget { | ||||
|   const CancelView({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return SingleChildScrollView( | ||||
|       child: Consumer<TransactionViewModel>( | ||||
|         builder: (context, provider, child) => Column( | ||||
|           children: [ | ||||
|             if (provider.listOnProcessTransaction.isEmpty) | ||||
|               EmptyCardTransaction( | ||||
|                 onPressedButton: () {}, | ||||
|               ), | ||||
|             ...provider.listOnProcessTransaction.map((e) { | ||||
|               return CardTransactionView( | ||||
|                 step: 'cancel', | ||||
|                 type: 'type', | ||||
|                 amount: 'amount', | ||||
|                 iconPath: 'iconPath', | ||||
|                 subs: 'subs', | ||||
|                 onTap: () {}, | ||||
|               ); | ||||
|             }), | ||||
|           ], | ||||
|         ), | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										37
									
								
								lib/features/transaction/view/done_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | ||||
| import 'package:cims_apps/application/component/card_transaction/card_transaction_view.dart'; | ||||
| import 'package:cims_apps/application/component/card_transaction/empty_card_transaction.dart'; | ||||
| import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class DoneView extends StatelessWidget { | ||||
|   const DoneView({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return SingleChildScrollView( | ||||
|       child: Consumer<TransactionViewModel>( | ||||
|         builder: (context, provider, child) { | ||||
|           return Column( | ||||
|             children: [ | ||||
|               if (provider.listOnProcessTransaction.isEmpty) | ||||
|                 EmptyCardTransaction( | ||||
|                   onPressedButton: () {}, | ||||
|                 ), | ||||
|               ...provider.listOnProcessTransaction.map((e) { | ||||
|                 return CardTransactionView( | ||||
|                   step: 'done', | ||||
|                   type: 'type', | ||||
|                   amount: 'amount', | ||||
|                   iconPath: 'iconPath', | ||||
|                   subs: 'subs', | ||||
|                   onTap: () {}, | ||||
|                 ); | ||||
|               }), | ||||
|             ], | ||||
|           ); | ||||
|         }, | ||||
|       ), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										34
									
								
								lib/features/transaction/view/onprocess_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,34 @@ | ||||
| import 'package:cims_apps/application/component/card_transaction/card_transaction_view.dart'; | ||||
| import 'package:cims_apps/application/component/card_transaction/empty_card_transaction.dart'; | ||||
| import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class OnProcessView extends StatelessWidget { | ||||
|   const OnProcessView({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return SingleChildScrollView( | ||||
|       child: | ||||
|           Consumer<TransactionViewModel>(builder: (context, provider, child) { | ||||
|         return Column(children: [ | ||||
|           if (provider.listOnProcessTransaction.isEmpty) | ||||
|             EmptyCardTransaction( | ||||
|               onPressedButton: () {}, | ||||
|             ), | ||||
|           ...provider.listOnProcessTransaction.map((e) { | ||||
|             return CardTransactionView( | ||||
|               step: 'on process', | ||||
|               type: 'type', | ||||
|               amount: 'amount', | ||||
|               iconPath: 'iconPath', | ||||
|               subs: 'subs', | ||||
|               onTap: () {}, | ||||
|             ); | ||||
|           }), | ||||
|         ]); | ||||
|       }), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										124
									
								
								lib/features/transaction/view/transaction_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,124 @@ | ||||
| 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:cims_apps/features/transaction/view/cancel_view.dart'; | ||||
| import 'package:cims_apps/features/transaction/view/done_view.dart'; | ||||
| import 'package:cims_apps/features/transaction/view/onprocess_view.dart'; | ||||
| import 'package:cims_apps/features/transaction/view/waiting_view.dart'; | ||||
| import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_toggle_tab/flutter_toggle_tab.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class TransactionView extends StatelessWidget { | ||||
|   const TransactionView({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     List<Tab> textTabs = const [ | ||||
|       Tab(text: 'Waiting'), | ||||
|       Tab(text: 'On process'), | ||||
|       Tab(text: 'Done'), | ||||
|       Tab(text: 'Cancel'), | ||||
|     ]; | ||||
|     List<Widget> listTabBarView = const [ | ||||
|       WaitingView(), | ||||
|       OnProcessView(), | ||||
|       DoneView(), | ||||
|       CancelView(), | ||||
|     ]; | ||||
|  | ||||
|     return ChangeNotifierProvider( | ||||
|         create: (context) => TransactionViewModel(), | ||||
|         builder: (context, child) { | ||||
|           return Scaffold( | ||||
|             backgroundColor: ColorPalette.primary, | ||||
|             body: SizedBox( | ||||
|               child: Stack( | ||||
|                 children: [ | ||||
|                   const ImageView(image: PathAssets.imgDashboardAccount), | ||||
|                   Column( | ||||
|                     children: [ | ||||
|                       SizedBox( | ||||
|                         height: SizeConfig.height * .05, | ||||
|                       ), | ||||
|                       const Center( | ||||
|                         child: Text( | ||||
|                           'Transaction', | ||||
|                           style: TextStyle( | ||||
|                               fontSize: 20, | ||||
|                               fontWeight: FontWeight.w700, | ||||
|                               color: Colors.white), | ||||
|                         ), | ||||
|                       ), | ||||
|                       SizedBox( | ||||
|                         height: SizeConfig.height * .04, | ||||
|                       ), | ||||
|                       Container( | ||||
|                         margin: const EdgeInsets.symmetric(horizontal: 24), | ||||
|                         child: FlutterToggleTab( | ||||
|                           height: SizeConfig.height * .065, | ||||
|                           width: SizeConfig.width * .2, | ||||
|                           marginSelected: const EdgeInsets.all(8.0), | ||||
|                           isScroll: false, | ||||
|                           selectedTextStyle: const TextStyle( | ||||
|                             color: ColorPalette.primary, | ||||
|                             fontWeight: FontWeight.w700, | ||||
|                           ), | ||||
|                           unSelectedTextStyle: const TextStyle( | ||||
|                             color: ColorPalette.blackFont, | ||||
|                             fontWeight: FontWeight.w700, | ||||
|                           ), | ||||
|                           unSelectedBackgroundColors: const [ | ||||
|                             ColorPalette.blue50 | ||||
|                           ], | ||||
|                           selectedBackgroundColors: const [ColorPalette.white], | ||||
|                           labels: const ['Subscribe', 'Reedem'], | ||||
|                           selectedLabelIndex: (p0) {}, | ||||
|                           selectedIndex: 0, | ||||
|                         ), | ||||
|                       ), | ||||
|                       Expanded( | ||||
|                           child: DefaultTabController( | ||||
|                         length: textTabs.length, | ||||
|                         child: Container( | ||||
|                           color: Colors.transparent, | ||||
|                           padding: const EdgeInsets.only(top: 32.0), | ||||
|                           child: Container( | ||||
|                             margin: const EdgeInsets.only(top: 24), | ||||
|                             padding: const EdgeInsets.only(top: 16.0), | ||||
|                             decoration: const BoxDecoration( | ||||
|                               color: Colors.white, | ||||
|                               borderRadius: BorderRadius.only( | ||||
|                                   topLeft: Radius.circular(24), | ||||
|                                   topRight: Radius.circular(24)), | ||||
|                             ), | ||||
|                             child: Column( | ||||
|                               children: [ | ||||
|                                 TabBar( | ||||
|                                   tabs: textTabs, | ||||
|                                   indicatorColor: Colors.blueAccent, | ||||
|                                 ), | ||||
|                                 Expanded( | ||||
|                                     child: Padding( | ||||
|                                   padding: const EdgeInsets.symmetric( | ||||
|                                     horizontal: 16.0, | ||||
|                                     vertical: 4.0, | ||||
|                                   ), | ||||
|                                   child: TabBarView(children: listTabBarView), | ||||
|                                 )) | ||||
|                               ], | ||||
|                             ), | ||||
|                           ), | ||||
|                         ), | ||||
|                       )), | ||||
|                     ], | ||||
|                   ) | ||||
|                 ], | ||||
|               ), | ||||
|             ), | ||||
|           ); | ||||
|         }); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										42
									
								
								lib/features/transaction/view/waiting_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,42 @@ | ||||
| import 'package:cims_apps/application/assets/path_assets.dart'; | ||||
| import 'package:cims_apps/application/component/card_transaction/card_transaction_view.dart'; | ||||
| import 'package:cims_apps/application/component/card_transaction/empty_card_transaction.dart'; | ||||
| import 'package:cims_apps/core/utils/number_formatter.dart'; | ||||
| import 'package:cims_apps/core/utils/string_utils.dart'; | ||||
| import 'package:cims_apps/features/transaction/viewmodel/transaction_viewmodel.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:provider/provider.dart'; | ||||
|  | ||||
| class WaitingView extends StatelessWidget { | ||||
|   const WaitingView({Key? key}) : super(key: key); | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|     return SingleChildScrollView( | ||||
|       child: | ||||
|           Consumer<TransactionViewModel>(builder: (context, provider, child) { | ||||
|         return Column( | ||||
|           children: [ | ||||
|             provider.listWaitingTransaction.isNotEmpty | ||||
|                 ? CardTransactionView( | ||||
|                     onTap: () {}, | ||||
|                     iconPath: PathAssets.iconEducation, | ||||
|                     type: 'Education', | ||||
|                     amount: NumberFormatter.numberCurrency( | ||||
|                       6000000, | ||||
|                       'Rp ', | ||||
|                       'id_ID', | ||||
|                       decimalDigits: 0, | ||||
|                     ), | ||||
|                     timeTransaction: StringUtils.formatTime(DateTime.now()), | ||||
|                     subs: '3 Subscription', | ||||
|                     step: 'waiting') | ||||
|                 : EmptyCardTransaction( | ||||
|                     onPressedButton: () {}, | ||||
|                   ), | ||||
|           ], | ||||
|         ); | ||||
|       }), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
|  | ||||
| class TransactionViewModel extends ChangeNotifier { | ||||
|   List listWaitingTransaction = [1]; | ||||
|   List listOnProcessTransaction = []; | ||||
|   List listDoneTransaction = []; | ||||
|   List listCancelTransaction = []; | ||||
| } | ||||
							
								
								
									
										114
									
								
								lib/main.dart
									
									
									
									
									
								
							
							
						
						| @@ -20,68 +20,66 @@ class MyApp extends StatelessWidget { | ||||
|       title: 'CIMS', | ||||
|       debugShowCheckedModeBanner: false, | ||||
|       theme: ThemeData( | ||||
|         appBarTheme: const AppBarTheme( | ||||
|             centerTitle: true, | ||||
|             backgroundColor: Colors.white, | ||||
|             elevation: 1, | ||||
|             foregroundColor: Colors.black, | ||||
|             titleTextStyle: TextStyle( | ||||
|               fontSize: 20, | ||||
|               fontWeight: FontWeight.w700, | ||||
|               fontFamily: 'Manrope', | ||||
|           appBarTheme: const AppBarTheme( | ||||
|               centerTitle: true, | ||||
|               backgroundColor: Colors.white, | ||||
|               elevation: 1, | ||||
|               foregroundColor: Colors.black, | ||||
|               titleTextStyle: TextStyle( | ||||
|                 fontSize: 20, | ||||
|                 fontWeight: FontWeight.w700, | ||||
|                 fontFamily: 'Manrope', | ||||
|                 color: ColorPalette.slate800, | ||||
|               )), | ||||
|           fontFamily: 'Manrope', | ||||
|           scaffoldBackgroundColor: Colors.white, | ||||
|           textTheme: const TextTheme( | ||||
|             displaySmall: TextStyle( | ||||
|               fontSize: 14, | ||||
|               fontWeight: FontWeight.w500, | ||||
|               color: ColorPalette.slate800, | ||||
|             )), | ||||
|         fontFamily: 'Manrope', | ||||
|         scaffoldBackgroundColor: Colors.white, | ||||
|         textTheme: const TextTheme( | ||||
|           displaySmall: TextStyle( | ||||
|             fontSize: 14, | ||||
|             fontWeight: FontWeight.w500, | ||||
|             color: ColorPalette.slate800, | ||||
|             ), | ||||
|             displayMedium: TextStyle( | ||||
|               fontSize: 16, | ||||
|               fontWeight: FontWeight.w600, | ||||
|               color: ColorPalette.slate800, | ||||
|             ), | ||||
|             displayLarge: TextStyle( | ||||
|               fontSize: 16, | ||||
|               fontWeight: FontWeight.bold, | ||||
|               color: ColorPalette.slate800, | ||||
|             ), | ||||
|             bodyMedium: TextStyle( | ||||
|               fontSize: 14, | ||||
|               fontWeight: FontWeight.w600, | ||||
|               color: ColorPalette.slate500, | ||||
|             ), | ||||
|             bodyLarge: TextStyle( | ||||
|               fontSize: 16, | ||||
|               fontWeight: FontWeight.bold, | ||||
|               color: ColorPalette.slate500, | ||||
|             ), | ||||
|             headlineSmall: TextStyle( | ||||
|               fontSize: 16, | ||||
|               fontWeight: FontWeight.bold, | ||||
|               color: ColorPalette.slate800, | ||||
|             ), | ||||
|             headlineLarge: TextStyle( | ||||
|               fontSize: 28, | ||||
|               fontWeight: FontWeight.bold, | ||||
|               color: ColorPalette.slate800, | ||||
|             ), | ||||
|           ), | ||||
|           displayMedium: TextStyle( | ||||
|             fontSize: 16, | ||||
|             fontWeight: FontWeight.w600, | ||||
|             color: ColorPalette.slate800, | ||||
|           colorScheme: const ColorScheme.light().copyWith( | ||||
|             primary: const Color(0xff2563EB), | ||||
|             onPrimary: const Color(0xFFFF9130), | ||||
|             secondary: const Color(0xFFFECDA6), | ||||
|             onBackground: const Color(0xFFA9A9A9), | ||||
|           ), | ||||
|           displayLarge: TextStyle( | ||||
|             fontSize: 16, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             color: ColorPalette.slate800, | ||||
|           bottomSheetTheme: const BottomSheetThemeData( | ||||
|               backgroundColor: Colors.white, surfaceTintColor: Colors.white) | ||||
|           // useMaterial3: true, | ||||
|           ), | ||||
|           bodyMedium: TextStyle( | ||||
|             fontSize: 14, | ||||
|             fontWeight: FontWeight.w600, | ||||
|             color: ColorPalette.slate500, | ||||
|           ), | ||||
|           bodyLarge: TextStyle( | ||||
|             fontSize: 16, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             color: ColorPalette.slate500, | ||||
|           ), | ||||
|           headlineSmall: TextStyle( | ||||
|             fontSize: 16, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             color: ColorPalette.slate800, | ||||
|           ), | ||||
|           headlineLarge: TextStyle( | ||||
|             fontSize: 28, | ||||
|             fontWeight: FontWeight.bold, | ||||
|             color: ColorPalette.slate800, | ||||
|           ), | ||||
|         ), | ||||
|         colorScheme: const ColorScheme.light().copyWith( | ||||
|           primary: const Color(0xff2563EB), | ||||
|           onPrimary: const Color(0xFFFF9130), | ||||
|           secondary: const Color(0xFFFECDA6), | ||||
|           onBackground: const Color(0xFFA9A9A9), | ||||
|         ), | ||||
|         bottomSheetTheme: BottomSheetThemeData( | ||||
|           backgroundColor: Colors.white, | ||||
|           surfaceTintColor: Colors.white | ||||
|         ) | ||||
|         // useMaterial3: true, | ||||
|       ), | ||||
|       initialRoute: initialRoute, | ||||
|       onGenerateRoute: generateRoutes, | ||||
|       navigatorObservers: [ | ||||
|   | ||||
							
								
								
									
										76
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						| @@ -49,6 +49,14 @@ packages: | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.1.1" | ||||
|   calendar_date_picker2: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: calendar_date_picker2 | ||||
|       sha256: b91d51b8d0928f9745e0113e86d06b161ac48c52b7530337a3b77283cbc6be27 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.5.3" | ||||
|   camera: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -251,11 +259,67 @@ packages: | ||||
|     description: flutter | ||||
|     source: sdk | ||||
|     version: "0.0.0" | ||||
|   flutter_toggle_tab: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: flutter_toggle_tab | ||||
|       sha256: "90ad0d050f656df677998825f985637d010117a1793828cd7e6dadada4ecd2c5" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "1.4.1" | ||||
|   flutter_web_plugins: | ||||
|     dependency: transitive | ||||
|     description: flutter | ||||
|     source: sdk | ||||
|     version: "0.0.0" | ||||
|   google_identity_services_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: google_identity_services_web | ||||
|       sha256: "0c56c2c5d60d6dfaf9725f5ad4699f04749fb196ee5a70487a46ef184837ccf6" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.3.0+2" | ||||
|   google_sign_in: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: google_sign_in | ||||
|       sha256: "0b8787cb9c1a68ad398e8010e8c8766bfa33556d2ab97c439fb4137756d7308f" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.2.1" | ||||
|   google_sign_in_android: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: google_sign_in_android | ||||
|       sha256: bfd42c81c30c6faba16e0f62968d5505a87504aaa672b3155ee931461abb0a49 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "6.1.21" | ||||
|   google_sign_in_ios: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: google_sign_in_ios | ||||
|       sha256: f3336d9e44d4d28063ac90271f6db5caf99f0480cb07281330e7a432edb95226 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "5.7.3" | ||||
|   google_sign_in_platform_interface: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: google_sign_in_platform_interface | ||||
|       sha256: "1f6e5787d7a120cc0359ddf315c92309069171306242e181c09472d1b00a2971" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "2.4.5" | ||||
|   google_sign_in_web: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: google_sign_in_web | ||||
|       sha256: a278ea2d01013faf341cbb093da880d0f2a552bbd1cb6ee90b5bebac9ba69d77 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.12.3+2" | ||||
|   group_button: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
| @@ -292,10 +356,10 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: js | ||||
|       sha256: "4186c61b32f99e60f011f7160e32c89a758ae9b1d0c6d28e2c02ef0382300e2b" | ||||
|       sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "0.7.0" | ||||
|     version: "0.7.1" | ||||
|   lints: | ||||
|     dependency: transitive | ||||
|     description: | ||||
| @@ -625,18 +689,18 @@ packages: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|       name: syncfusion_flutter_core | ||||
|       sha256: e8580e201c7197feac830b501889e877796a9fabbe20dcdbe90a981603939101 | ||||
|       sha256: "9f98e2726af42967497eaef68f3373261700bbfcd33bd97da4ec85cb56fcdaf7" | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "24.2.4" | ||||
|     version: "24.2.7" | ||||
|   syncfusion_flutter_signaturepad: | ||||
|     dependency: "direct main" | ||||
|     description: | ||||
|       name: syncfusion_flutter_signaturepad | ||||
|       sha256: "878e1063b909a83c83677627261780d42d532d0b5e7e259d84da805008e7fb0d" | ||||
|       sha256: d51d5e346c70b938a8e1f2318a073213172aea7b99e33073c379657b1066c001 | ||||
|       url: "https://pub.dev" | ||||
|     source: hosted | ||||
|     version: "24.2.4" | ||||
|     version: "24.2.7" | ||||
|   synchronized: | ||||
|     dependency: transitive | ||||
|     description: | ||||
|   | ||||
| @@ -51,6 +51,10 @@ dependencies: | ||||
|   syncfusion_flutter_signaturepad: ^24.2.4 | ||||
|   dotted_border: ^2.1.0 | ||||
|   shared_preferences: ^2.2.2 | ||||
|   calendar_date_picker2: ^0.5.3 | ||||
|   google_sign_in: ^6.2.1 | ||||
|   flutter_toggle_tab: ^1.4.1 | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||