bayu/dev #2
							
								
								
									
										
											BIN
										
									
								
								assets/icons/icon-flag.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/icons/icon-flag.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 2.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/images/img-success-signup.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/images/img-success-signup.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 88 KiB | 
| @@ -11,6 +11,7 @@ class PathAssets { | |||||||
|   static const String icon1 = 'assets/icons/icon-1.png'; |   static const String icon1 = 'assets/icons/icon-1.png'; | ||||||
|   static const String iconConnect = 'assets/icons/icon-connect.png'; |   static const String iconConnect = 'assets/icons/icon-connect.png'; | ||||||
|   static const String iconShield = 'assets/icons/icon-shield.png'; |   static const String iconShield = 'assets/icons/icon-shield.png'; | ||||||
|  |   static const String iconFlag = 'assets/icons/icon-flag.png'; | ||||||
|  |  | ||||||
|   /// IMAGE |   /// IMAGE | ||||||
|   static const String imgSplashLogo = 'assets/images/splash-logo.png'; |   static const String imgSplashLogo = 'assets/images/splash-logo.png'; | ||||||
| @@ -23,4 +24,5 @@ class PathAssets { | |||||||
|   static const String imgKtpCropped = 'assets/images/img-ktp-cropped.png'; |   static const String imgKtpCropped = 'assets/images/img-ktp-cropped.png'; | ||||||
|   static const String imgKtpClear = 'assets/images/img-ktp-clear.png'; |   static const String imgKtpClear = 'assets/images/img-ktp-clear.png'; | ||||||
|   static const String imgKtpBlur = 'assets/images/img-ktp-blur.png'; |   static const String imgKtpBlur = 'assets/images/img-ktp-blur.png'; | ||||||
|  |   static const String imgSuccessSignup = 'assets/images/img-success-signup.png'; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -55,8 +55,9 @@ class ButtonView extends StatelessWidget { | |||||||
|     final widthPrefix = |     final widthPrefix = | ||||||
|         this.widthPrefix ?? (heightWrapContent ? width! / 4.7 : _widthBtn / 16); |         this.widthPrefix ?? (heightWrapContent ? width! / 4.7 : _widthBtn / 16); | ||||||
|  |  | ||||||
|     return Container( |     return Center( | ||||||
|       margin: EdgeInsets.symmetric(vertical: marginVertical ?? 32.0), |       child: Container( | ||||||
|  |         margin: EdgeInsets.symmetric(vertical: marginVertical ?? 24.0), | ||||||
|         width: width ?? _widthBtn, |         width: width ?? _widthBtn, | ||||||
|         height: heightWrapContent ? null : height ?? _heightBtn, |         height: heightWrapContent ? null : height ?? _heightBtn, | ||||||
|         child: ElevatedButton( |         child: ElevatedButton( | ||||||
| @@ -75,7 +76,7 @@ class ButtonView extends StatelessWidget { | |||||||
|               side: isOutlined |               side: isOutlined | ||||||
|                   ? BorderSide( |                   ? BorderSide( | ||||||
|                       color: disabled |                       color: disabled | ||||||
|                         ? color.surface |                           ? ColorPalette.greyBorder | ||||||
|                           : isSecondaryColor |                           : isSecondaryColor | ||||||
|                               ? ColorPalette.greyBorder |                               ? ColorPalette.greyBorder | ||||||
|                               : ColorPalette.primary, |                               : ColorPalette.primary, | ||||||
| @@ -129,6 +130,7 @@ class ButtonView extends StatelessWidget { | |||||||
|             ], |             ], | ||||||
|           ), |           ), | ||||||
|         ), |         ), | ||||||
|  |       ), | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										146
									
								
								lib/application/component/otp/otp_view.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								lib/application/component/otp/otp_view.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,146 @@ | |||||||
|  | import 'package:cims_apps/application/component/otp/otp_viewmodel.dart'; | ||||||
|  | import 'package:cims_apps/application/component/text_caption/text_caption.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/registration_password_view.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:pinput/pinput.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  |  | ||||||
|  | class OtpView extends StatelessWidget { | ||||||
|  |   final String title; | ||||||
|  |   final String? contentTitle, contentSubtitle; | ||||||
|  |   const OtpView({ | ||||||
|  |     Key? key, | ||||||
|  |     required this.title, | ||||||
|  |     this.contentTitle, | ||||||
|  |     this.contentSubtitle, | ||||||
|  |   }) : super(key: key); | ||||||
|  |  | ||||||
|  |   Widget _otpContent(BuildContext context, OtpViewModel provider) { | ||||||
|  |     return Form( | ||||||
|  |         key: provider.formKey, | ||||||
|  |         child: Column( | ||||||
|  |           children: [ | ||||||
|  |             Pinput( | ||||||
|  |               length: 4, | ||||||
|  |               controller: provider.ctrlPin, | ||||||
|  |               focusNode: provider.focusNode, | ||||||
|  |               mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|  |               validator: (value) { | ||||||
|  |                 if (value!.isEmpty) { | ||||||
|  |                   return 'Pin must be complete'; | ||||||
|  |                 } | ||||||
|  |                 return null; | ||||||
|  |               }, | ||||||
|  |               defaultPinTheme: PinTheme( | ||||||
|  |                 textStyle: const TextStyle( | ||||||
|  |                   color: ColorPalette.slate800, | ||||||
|  |                   fontSize: 24, | ||||||
|  |                   fontWeight: FontWeight.bold, | ||||||
|  |                 ), | ||||||
|  |                 width: SizeConfig.width * .19, | ||||||
|  |                 height: SizeConfig.height * .08, | ||||||
|  |                 decoration: BoxDecoration( | ||||||
|  |                   border: Border.all(color: const Color(0xFFE2E8F0)), | ||||||
|  |                   borderRadius: BorderRadius.circular(8), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |               errorPinTheme: PinTheme( | ||||||
|  |                 textStyle: const TextStyle( | ||||||
|  |                   color: ColorPalette.slate800, | ||||||
|  |                   fontSize: 24, | ||||||
|  |                   fontWeight: FontWeight.bold, | ||||||
|  |                 ), | ||||||
|  |                 width: SizeConfig.width * .19, | ||||||
|  |                 height: SizeConfig.height * .08, | ||||||
|  |                 decoration: BoxDecoration( | ||||||
|  |                   border: Border.all(color: Colors.redAccent), | ||||||
|  |                   borderRadius: BorderRadius.circular(8), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |               onCompleted: (pin) => provider.enableButton(), | ||||||
|  |               onChanged: (value) { | ||||||
|  |                 if (provider.ctrlPin.length != 4) { | ||||||
|  |                   provider.enableButton(isActive: false); | ||||||
|  |                 } | ||||||
|  |               }, | ||||||
|  |             ), | ||||||
|  |             Container( | ||||||
|  |               margin: const EdgeInsets.symmetric(vertical: 32.0), | ||||||
|  |               width: SizeConfig.width, | ||||||
|  |               height: SizeConfig.height * .07, | ||||||
|  |               child: ElevatedButton( | ||||||
|  |                 style: ElevatedButton.styleFrom( | ||||||
|  |                   elevation: 0, | ||||||
|  |                   backgroundColor: ColorPalette.primary, | ||||||
|  |                 ), | ||||||
|  |                 onPressed: !provider.buttonIsActive | ||||||
|  |                     ? null | ||||||
|  |                     : () { | ||||||
|  |                         if (provider.formKey.currentState!.validate()) { | ||||||
|  |                           final pin = provider.ctrlPin.text; | ||||||
|  |                           provider.validateOtp(pin).then((value) { | ||||||
|  |                             if (value) { | ||||||
|  |                               routePush(context, | ||||||
|  |                                   page: const RegistrationPasswordView(), | ||||||
|  |                                   routeType: RouteType.pushReplace); | ||||||
|  |                             } else { | ||||||
|  |                               provider.ctrlPin.clear(); | ||||||
|  |                             } | ||||||
|  |                           }); | ||||||
|  |                         } | ||||||
|  |                       }, | ||||||
|  |                 child: const Text( | ||||||
|  |                   'Verify', | ||||||
|  |                   style: TextStyle( | ||||||
|  |                     fontSize: 16, | ||||||
|  |                     fontWeight: FontWeight.bold, | ||||||
|  |                     color: Colors.white, | ||||||
|  |                   ), | ||||||
|  |                 ), | ||||||
|  |               ), | ||||||
|  |             ), | ||||||
|  |           ], | ||||||
|  |         )); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return ChangeNotifierProvider( | ||||||
|  |         create: (context) => OtpViewModel(), | ||||||
|  |         builder: (context, child) { | ||||||
|  |           return Scaffold( | ||||||
|  |             appBar: AppBar( | ||||||
|  |               title: Text(title), | ||||||
|  |             ), | ||||||
|  |             body: Container( | ||||||
|  |               padding: const EdgeInsets.all(16.0), | ||||||
|  |               child: | ||||||
|  |                   Consumer<OtpViewModel>(builder: (context, provider, child) { | ||||||
|  |                 return Column( | ||||||
|  |                   children: [ | ||||||
|  |                     TextCaption( | ||||||
|  |                       title: contentTitle ?? '', | ||||||
|  |                       subtitle: contentSubtitle ?? '', | ||||||
|  |                     ), | ||||||
|  |                     _otpContent(context, provider), | ||||||
|  |                     TextButton( | ||||||
|  |                         onPressed: () { | ||||||
|  |                           provider.ctrlPin.clear(); | ||||||
|  |                         }, | ||||||
|  |                         child: const Text( | ||||||
|  |                           'Resend Code', | ||||||
|  |                           style: TextStyle( | ||||||
|  |                             fontWeight: FontWeight.w700, | ||||||
|  |                           ), | ||||||
|  |                         )) | ||||||
|  |                   ], | ||||||
|  |                 ); | ||||||
|  |               }), | ||||||
|  |             ), | ||||||
|  |           ); | ||||||
|  |         }); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								lib/application/component/otp/otp_viewmodel.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								lib/application/component/otp/otp_viewmodel.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  |  | ||||||
|  | class OtpViewModel extends ChangeNotifier { | ||||||
|  |   var formKey = GlobalKey<FormState>(); | ||||||
|  |   var focusNode = FocusNode(); | ||||||
|  |   bool buttonIsActive = false; | ||||||
|  |  | ||||||
|  |   TextEditingController ctrlPin = TextEditingController(); | ||||||
|  |  | ||||||
|  |   Future<bool> validateOtp(String pin) async { | ||||||
|  |     final pinLength = pin.length; | ||||||
|  |     if (pinLength == 4) { | ||||||
|  |       return true; | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void enableButton({bool isActive = true}) { | ||||||
|  |     buttonIsActive = isActive; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -3,10 +3,14 @@ import 'package:flutter/material.dart'; | |||||||
|  |  | ||||||
| class TextCaption extends StatelessWidget { | class TextCaption extends StatelessWidget { | ||||||
|   final String title, subtitle; |   final String title, subtitle; | ||||||
|  |   final TextAlign? textAlignSubtitle; | ||||||
|  |   final CrossAxisAlignment? crossAxisAlignment; | ||||||
|   const TextCaption({ |   const TextCaption({ | ||||||
|     Key? key, |     Key? key, | ||||||
|     required this.title, |     required this.title, | ||||||
|     this.subtitle = '', |     this.subtitle = '', | ||||||
|  |     this.textAlignSubtitle, | ||||||
|  |     this.crossAxisAlignment, | ||||||
|   }) : super(key: key); |   }) : super(key: key); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -14,7 +18,7 @@ class TextCaption extends StatelessWidget { | |||||||
|     return Padding( |     return Padding( | ||||||
|       padding: const EdgeInsets.only(bottom: 32.0), |       padding: const EdgeInsets.only(bottom: 32.0), | ||||||
|       child: Column( |       child: Column( | ||||||
|         crossAxisAlignment: CrossAxisAlignment.start, |         crossAxisAlignment: crossAxisAlignment ?? CrossAxisAlignment.start, | ||||||
|         mainAxisAlignment: MainAxisAlignment.spaceBetween, |         mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||||||
|         children: [ |         children: [ | ||||||
|           Text( |           Text( | ||||||
| @@ -30,10 +34,11 @@ class TextCaption extends StatelessWidget { | |||||||
|                   padding: const EdgeInsets.only(top: 8.0), |                   padding: const EdgeInsets.only(top: 8.0), | ||||||
|                   child: Text( |                   child: Text( | ||||||
|                     subtitle, |                     subtitle, | ||||||
|  |                     textAlign: textAlignSubtitle ?? TextAlign.start, | ||||||
|                     style: const TextStyle( |                     style: const TextStyle( | ||||||
|                       fontSize: 16, |                       fontSize: 16, | ||||||
|                       fontWeight: FontWeight.w500, |                       fontWeight: FontWeight.w500, | ||||||
|                       color: ColorPalette.slate800, |                       color: ColorPalette.slate500, | ||||||
|                     ), |                     ), | ||||||
|                   ), |                   ), | ||||||
|                 ) |                 ) | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ class TextFormView extends StatelessWidget { | |||||||
|   final String? hintText, errorText; |   final String? hintText, errorText; | ||||||
|   final TextEditingController? ctrl; |   final TextEditingController? ctrl; | ||||||
|   final Widget? suffixIcon, suffixLable; |   final Widget? suffixIcon, suffixLable; | ||||||
|   final Widget? prefixIcon; |   final Widget? prefixIcon, prefix; | ||||||
|   final TextInputType? keyboardType; |   final TextInputType? keyboardType; | ||||||
|   final FormFieldValidator<String>? validator; |   final FormFieldValidator<String>? validator; | ||||||
|   final bool obscureText; |   final bool obscureText; | ||||||
| @@ -70,7 +70,8 @@ class TextFormView extends StatelessWidget { | |||||||
|       this.disableColor = false, |       this.disableColor = false, | ||||||
|       this.enableInteractiveSelection = true, |       this.enableInteractiveSelection = true, | ||||||
|       this.focusNode, |       this.focusNode, | ||||||
|       this.isTextAlignCenter = false}) |       this.isTextAlignCenter = false, | ||||||
|  |       this.prefix}) | ||||||
|       : super(key: key); |       : super(key: key); | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
| @@ -94,12 +95,12 @@ class TextFormView extends StatelessWidget { | |||||||
|                             name, |                             name, | ||||||
|                             style: const TextStyle( |                             style: const TextStyle( | ||||||
|                               fontSize: 16, |                               fontSize: 16, | ||||||
|                               color: ColorPalette.greyLight, |                               // color: ColorPalette.greyLight, | ||||||
|                             ), |                             ), | ||||||
|                           ), |                           ), | ||||||
|                           suffixLable ?? |                           suffixLable ?? | ||||||
|                               const Text( |                               const Text( | ||||||
|                                 " * ", |                                 "", | ||||||
|                                 style: TextStyle( |                                 style: TextStyle( | ||||||
|                                   fontSize: 16, |                                   fontSize: 16, | ||||||
|                                   color: Colors.red, |                                   color: Colors.red, | ||||||
| @@ -187,7 +188,8 @@ class TextFormView extends StatelessWidget { | |||||||
|               prefixIcon: prefixIcon, |               prefixIcon: prefixIcon, | ||||||
|               suffixIconConstraints: suffixIconConstraints, |               suffixIconConstraints: suffixIconConstraints, | ||||||
|               prefixIconConstraints: preffixIconConstraints, |               prefixIconConstraints: preffixIconConstraints, | ||||||
|           ), |               prefix: prefix, | ||||||
|  |               contentPadding: EdgeInsets.zero), | ||||||
|         ) |         ) | ||||||
|       ], |       ], | ||||||
|     ); |     ); | ||||||
|   | |||||||
| @@ -73,5 +73,6 @@ class ColorPalette { | |||||||
|   static const Color chathamsBlue = Color(0xFF285BB9); |   static const Color chathamsBlue = Color(0xFF285BB9); | ||||||
|   static const Color background = Color(0xFFDADADA); |   static const Color background = Color(0xFFDADADA); | ||||||
|   static const Color backgroundBlueLight = Color(0xFFEBF3FD); |   static const Color backgroundBlueLight = Color(0xFFEBF3FD); | ||||||
|  |   static const Color slate500 = Color(0xFF64748B); | ||||||
|   static const Color slate800 = Color(0xFF1E293B); |   static const Color slate800 = Color(0xFF1E293B); | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										16
									
								
								lib/core/utils/string_utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								lib/core/utils/string_utils.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | class StringUtils { | ||||||
|  |   static bool emailValidation(String email) { | ||||||
|  |     return RegExp( | ||||||
|  |             r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$') | ||||||
|  |         .hasMatch(email); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static bool phoneValidation(String phone) { | ||||||
|  |     return RegExp(r'^(\+62|62|0)8[1-9][0-9]{6,10}$').hasMatch(phone); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   static bool passwordValidation(String password) { | ||||||
|  |     return RegExp(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*?[\W_])(?=.{8,})') | ||||||
|  |         .hasMatch(password); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -0,0 +1,151 @@ | |||||||
|  | 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_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/submission_parent.dart'; | ||||||
|  | import 'package:cims_apps/features/auth/registration/viewmodel/registration_viewmodel.dart'; | ||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  |  | ||||||
|  | class RegistrationPasswordView extends StatelessWidget { | ||||||
|  |   const RegistrationPasswordView({Key? key}) : super(key: key); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return ChangeNotifierProvider( | ||||||
|  |         create: (context) => RegistrationViewModel(), | ||||||
|  |         builder: (context, child) { | ||||||
|  |           return Scaffold( | ||||||
|  |             appBar: AppBar( | ||||||
|  |               title: const Text('Sign Up'), | ||||||
|  |             ), | ||||||
|  |             body: SingleChildScrollView( | ||||||
|  |               padding: const EdgeInsets.all(16.0), | ||||||
|  |               child: Consumer<RegistrationViewModel>( | ||||||
|  |                   builder: (context, provider, child) { | ||||||
|  |                 return Form( | ||||||
|  |                   key: provider.formKey, | ||||||
|  |                   child: Column( | ||||||
|  |                     crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|  |                     children: [ | ||||||
|  |                       const TextCaption( | ||||||
|  |                         title: 'Create your password', | ||||||
|  |                         subtitle: | ||||||
|  |                             'The password you create serves as your login', | ||||||
|  |                       ), | ||||||
|  |                       TextFormView( | ||||||
|  |                         name: 'Password', | ||||||
|  |                         hintText: 'Input password', | ||||||
|  |                         ctrl: provider.passwordCtrl, | ||||||
|  |                         obscureText: !provider.showPassword, | ||||||
|  |                         validator: (value) { | ||||||
|  |                           if (value!.isEmpty) { | ||||||
|  |                             return 'Password must filled'; | ||||||
|  |                           } else { | ||||||
|  |                             return null; | ||||||
|  |                           } | ||||||
|  |                         }, | ||||||
|  |                         suffixIcon: GestureDetector( | ||||||
|  |                           onTap: () { | ||||||
|  |                             provider.toggleVisibility(); | ||||||
|  |                           }, | ||||||
|  |                           child: Icon( | ||||||
|  |                             provider.showPassword | ||||||
|  |                                 ? Icons.visibility_outlined | ||||||
|  |                                 : Icons.visibility_off_outlined, | ||||||
|  |                             color: ColorPalette.greyDarker, | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                       const SizedBox( | ||||||
|  |                         height: 32.0, | ||||||
|  |                       ), | ||||||
|  |                       TextFormView( | ||||||
|  |                         name: 'Confirm Password', | ||||||
|  |                         hintText: 'Input password', | ||||||
|  |                         ctrl: provider.confirmPasswordCtrl, | ||||||
|  |                         obscureText: !provider.showPasswordConfirm, | ||||||
|  |                         validator: (value) { | ||||||
|  |                           if (value!.isEmpty) { | ||||||
|  |                             return 'Password must filled'; | ||||||
|  |                           } else if (value != provider.passwordCtrl.text) { | ||||||
|  |                             return 'Password must be same'; | ||||||
|  |                           } else { | ||||||
|  |                             return null; | ||||||
|  |                           } | ||||||
|  |                         }, | ||||||
|  |                         suffixIcon: GestureDetector( | ||||||
|  |                           onTap: () { | ||||||
|  |                             provider.toggleVisibilityConfirm(); | ||||||
|  |                           }, | ||||||
|  |                           child: Icon( | ||||||
|  |                             provider.showPasswordConfirm | ||||||
|  |                                 ? Icons.visibility_outlined | ||||||
|  |                                 : Icons.visibility_off_outlined, | ||||||
|  |                             color: ColorPalette.greyDarker, | ||||||
|  |                           ), | ||||||
|  |                         ), | ||||||
|  |                       ), | ||||||
|  |                       ButtonView( | ||||||
|  |                         name: 'Confirm', | ||||||
|  |                         onPressed: () { | ||||||
|  |                           if (provider.formKey.currentState!.validate()) { | ||||||
|  |                             routePush(context, page: const DialogSuccess()); | ||||||
|  |                           } | ||||||
|  |                         }, | ||||||
|  |                       ) | ||||||
|  |                     ], | ||||||
|  |                   ), | ||||||
|  |                 ); | ||||||
|  |               }), | ||||||
|  |             ), | ||||||
|  |           ); | ||||||
|  |         }); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | class DialogSuccess extends StatelessWidget { | ||||||
|  |   const DialogSuccess({Key? key}) : super(key: key); | ||||||
|  |  | ||||||
|  |   @override | ||||||
|  |   Widget build(BuildContext context) { | ||||||
|  |     return Scaffold( | ||||||
|  |       body: Container( | ||||||
|  |         padding: const EdgeInsets.all(24.0), | ||||||
|  |         child: Column( | ||||||
|  |           mainAxisAlignment: MainAxisAlignment.end, | ||||||
|  |           crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |           children: [ | ||||||
|  |             ImageView( | ||||||
|  |               image: PathAssets.imgSuccessSignup, | ||||||
|  |               width: SizeConfig.width * .8, | ||||||
|  |             ), | ||||||
|  |             const TextCaption( | ||||||
|  |               title: 'Success', | ||||||
|  |               subtitle: | ||||||
|  |                   'Congratulations, your account creation was successful!', | ||||||
|  |               crossAxisAlignment: CrossAxisAlignment.center, | ||||||
|  |               textAlignSubtitle: TextAlign.center, | ||||||
|  |             ), | ||||||
|  |             SizedBox( | ||||||
|  |               height: SizeConfig.height * .2, | ||||||
|  |             ), | ||||||
|  |             ButtonView( | ||||||
|  |               name: 'Next', | ||||||
|  |               marginVertical: 8.0, | ||||||
|  |               onPressed: () { | ||||||
|  |                 routePush(context, | ||||||
|  |                     page: const SubmissionParent(), | ||||||
|  |                     routeType: RouteType.pushReplace); | ||||||
|  |               }, | ||||||
|  |             ) | ||||||
|  |           ], | ||||||
|  |         ), | ||||||
|  |       ), | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -1,10 +1,16 @@ | |||||||
|  | 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/button/button_view.dart'; | ||||||
|  | import 'package:cims_apps/application/component/image/image_view.dart'; | ||||||
|  | import 'package:cims_apps/application/component/otp/otp_view.dart'; | ||||||
| import 'package:cims_apps/application/component/text_caption/text_caption.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/component/text_form/text_form_view.dart'; | ||||||
| import 'package:cims_apps/core/route/route.dart'; | import 'package:cims_apps/application/theme/color_palette.dart'; | ||||||
| import 'package:cims_apps/features/auth/registration/view/initial_registration_step.dart'; | import 'package:cims_apps/core/utils/size_config.dart'; | ||||||
|  | import 'package:cims_apps/features/auth/registration/viewmodel/registration_viewmodel.dart'; | ||||||
| import 'package:flutter/gestures.dart'; | import 'package:flutter/gestures.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
|  | import 'package:flutter/services.dart'; | ||||||
|  | import 'package:provider/provider.dart'; | ||||||
|  |  | ||||||
| class RegistrationView extends StatelessWidget { | class RegistrationView extends StatelessWidget { | ||||||
|   static const routName = '/RegistrationView'; |   static const routName = '/RegistrationView'; | ||||||
| @@ -12,12 +18,43 @@ class RegistrationView extends StatelessWidget { | |||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|  |     showOtpWidget() { | ||||||
|  |       Navigator.of(context).pop(); | ||||||
|  |       showModalBottomSheet( | ||||||
|  |         context: context, | ||||||
|  |         isScrollControlled: true, | ||||||
|  |         enableDrag: false, | ||||||
|  |         builder: (BuildContext context) { | ||||||
|  |           return Padding( | ||||||
|  |             padding: EdgeInsets.only( | ||||||
|  |               top: MediaQueryData.fromView( | ||||||
|  |                 WidgetsBinding.instance.window, | ||||||
|  |               ).padding.top, | ||||||
|  |             ), | ||||||
|  |             child: const OtpView( | ||||||
|  |               title: 'Sign Up', | ||||||
|  |               contentTitle: 'Check your SMS', | ||||||
|  |               contentSubtitle: | ||||||
|  |                   'Enter 4 digit code We’ve sent to verify your phone number', | ||||||
|  |             ), | ||||||
|  |           ); | ||||||
|  |         }, | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return ChangeNotifierProvider( | ||||||
|  |         create: (context) => RegistrationViewModel(), | ||||||
|  |         builder: (context, child) { | ||||||
|           return Scaffold( |           return Scaffold( | ||||||
|             appBar: AppBar( |             appBar: AppBar( | ||||||
|               title: const Text('Sign Up'), |               title: const Text('Sign Up'), | ||||||
|             ), |             ), | ||||||
|             body: Container( |             body: Container( | ||||||
|               padding: const EdgeInsets.all(24.0), |               padding: const EdgeInsets.all(24.0), | ||||||
|  |               child: Consumer<RegistrationViewModel>( | ||||||
|  |                   builder: (context, provider, child) { | ||||||
|  |                 return Form( | ||||||
|  |                   key: provider.formKeyPhone, | ||||||
|                   child: Column( |                   child: Column( | ||||||
|                     crossAxisAlignment: CrossAxisAlignment.start, |                     crossAxisAlignment: CrossAxisAlignment.start, | ||||||
|                     children: [ |                     children: [ | ||||||
| @@ -25,11 +62,56 @@ class RegistrationView extends StatelessWidget { | |||||||
|                         title: 'Enter your phone number', |                         title: 'Enter your phone number', | ||||||
|                         subtitle: 'Input your registered phone number', |                         subtitle: 'Input your registered phone number', | ||||||
|                       ), |                       ), | ||||||
|             TextFormView(name: 'Phone Number'), |                       TextFormView( | ||||||
|  |                         name: 'Phone Number', | ||||||
|  |                         keyboardType: TextInputType.number, | ||||||
|  |                         inputFormatters: [ | ||||||
|  |                           FilteringTextInputFormatter.deny(RegExp(r'^0')) | ||||||
|  |                         ], | ||||||
|  |                         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, | ||||||
|  |                                   ), | ||||||
|  |                                 ) | ||||||
|  |                               ], | ||||||
|  |                             )), | ||||||
|  |                         ctrl: provider.phoneNumberCtrl, | ||||||
|  |                         validator: (value) { | ||||||
|  |                           if (value!.isEmpty) { | ||||||
|  |                             return 'Phone number must be filled'; | ||||||
|  |                           } else { | ||||||
|  |                             return null; | ||||||
|  |                           } | ||||||
|  |                         }, | ||||||
|  |                       ), | ||||||
|                       ButtonView( |                       ButtonView( | ||||||
|                         name: 'Next', |                         name: 'Next', | ||||||
|                         onPressed: () { |                         onPressed: () { | ||||||
|                 routePush(context, page: const InitialRegistrationStep()); |                           if (provider.formKeyPhone.currentState!.validate()) { | ||||||
|  |                             showOtpWidget(); | ||||||
|  |                           } | ||||||
|                         }, |                         }, | ||||||
|                       ), |                       ), | ||||||
|                       Align( |                       Align( | ||||||
| @@ -45,10 +127,7 @@ class RegistrationView extends StatelessWidget { | |||||||
|                               ), |                               ), | ||||||
|                             ), |                             ), | ||||||
|                             TextSpan( |                             TextSpan( | ||||||
|                     recognizer: TapGestureRecognizer() |                               recognizer: TapGestureRecognizer()..onTap = () {}, | ||||||
|                       ..onTap = () { |  | ||||||
|                         print('object'); |  | ||||||
|                       }, |  | ||||||
|                               text: ' Sign In', |                               text: ' Sign In', | ||||||
|                               style: const TextStyle( |                               style: const TextStyle( | ||||||
|                                 color: Colors.blue, |                                 color: Colors.blue, | ||||||
| @@ -59,7 +138,10 @@ class RegistrationView extends StatelessWidget { | |||||||
|                       ) |                       ) | ||||||
|                     ], |                     ], | ||||||
|                   ), |                   ), | ||||||
|  |                 ); | ||||||
|  |               }), | ||||||
|             ), |             ), | ||||||
|           ); |           ); | ||||||
|  |         }); | ||||||
|   } |   } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,10 +1,12 @@ | |||||||
| import 'package:cims_apps/application/component/button/button_view.dart'; | import 'package:cims_apps/application/component/button/button_view.dart'; | ||||||
| import 'package:cims_apps/application/theme/color_palette.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/core/utils/size_config.dart'; | ||||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/initial_take_photo.dart'; | import 'package:cims_apps/features/auth/registration/view/submission_data/initial_take_photo.dart'; | ||||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/submit_email.dart'; | import 'package:cims_apps/features/auth/registration/view/submission_data/submit_email.dart'; | ||||||
| import 'package:cims_apps/features/auth/registration/view/submission_data/submit_personal_data.dart'; | import 'package:cims_apps/features/auth/registration/view/submission_data/submit_personal_data.dart'; | ||||||
| import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | import 'package:cims_apps/features/auth/registration/viewmodel/submission_data_viewmodel.dart'; | ||||||
|  | import 'package:cims_apps/features/bottom_navigation_view.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:provider/provider.dart'; | import 'package:provider/provider.dart'; | ||||||
|  |  | ||||||
| @@ -71,7 +73,14 @@ class _SubmissionParentState extends State<SubmissionParent> { | |||||||
|     return ChangeNotifierProvider( |     return ChangeNotifierProvider( | ||||||
|         create: (context) => SubmissionDataViewModel(), |         create: (context) => SubmissionDataViewModel(), | ||||||
|         builder: (context, child) { |         builder: (context, child) { | ||||||
|           return Scaffold( |           return WillPopScope( | ||||||
|  |             onWillPop: () async { | ||||||
|  |               await routePush(context, | ||||||
|  |                   page: const BottomNavigationView(), | ||||||
|  |                   routeType: RouteType.pushReplace); | ||||||
|  |               return false; | ||||||
|  |             }, | ||||||
|  |             child: Scaffold( | ||||||
|               appBar: AppBar( |               appBar: AppBar( | ||||||
|                 title: const Text('Registration'), |                 title: const Text('Registration'), | ||||||
|               ), |               ), | ||||||
| @@ -91,14 +100,16 @@ class _SubmissionParentState extends State<SubmissionParent> { | |||||||
|                             children: List.generate( |                             children: List.generate( | ||||||
|                               provider.stepAmount, |                               provider.stepAmount, | ||||||
|                               (index) => _stepItem( |                               (index) => _stepItem( | ||||||
|                               isCurrentStep: provider.currentStep == index + 1, |                                 isCurrentStep: | ||||||
|  |                                     provider.currentStep == index + 1, | ||||||
|                               ), |                               ), | ||||||
|                             ), |                             ), | ||||||
|                           ), |                           ), | ||||||
|                         ), |                         ), | ||||||
|                         Expanded( |                         Expanded( | ||||||
|                           child: Container( |                           child: Container( | ||||||
|                           padding: const EdgeInsets.symmetric(horizontal: 16.0), |                             padding: | ||||||
|  |                                 const EdgeInsets.symmetric(horizontal: 16.0), | ||||||
|                             child: _content(provider.currentStep), |                             child: _content(provider.currentStep), | ||||||
|                           ), |                           ), | ||||||
|                         ), |                         ), | ||||||
| @@ -119,6 +130,7 @@ class _SubmissionParentState extends State<SubmissionParent> { | |||||||
|                   }), |                   }), | ||||||
|                 ], |                 ], | ||||||
|               ), |               ), | ||||||
|  |             ), | ||||||
|           ); |           ); | ||||||
|         }); |         }); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -0,0 +1,21 @@ | |||||||
|  | import 'package:flutter/material.dart'; | ||||||
|  |  | ||||||
|  | class RegistrationViewModel extends ChangeNotifier { | ||||||
|  |   TextEditingController passwordCtrl = TextEditingController(); | ||||||
|  |   TextEditingController confirmPasswordCtrl = TextEditingController(); | ||||||
|  |   TextEditingController phoneNumberCtrl = TextEditingController(); | ||||||
|  |   var formKey = GlobalKey<FormState>(); | ||||||
|  |   var formKeyPhone = GlobalKey<FormState>(); | ||||||
|  |   bool showPassword = false; | ||||||
|  |   bool showPasswordConfirm = false; | ||||||
|  |  | ||||||
|  |   void toggleVisibility() { | ||||||
|  |     showPassword = !showPassword; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   void toggleVisibilityConfirm() { | ||||||
|  |     showPasswordConfirm = !showPasswordConfirm; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								pubspec.lock
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								pubspec.lock
									
									
									
									
									
								
							| @@ -163,6 +163,11 @@ packages: | |||||||
|     description: flutter |     description: flutter | ||||||
|     source: sdk |     source: sdk | ||||||
|     version: "0.0.0" |     version: "0.0.0" | ||||||
|  |   flutter_web_plugins: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: flutter | ||||||
|  |     source: sdk | ||||||
|  |     version: "0.0.0" | ||||||
|   http: |   http: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -323,6 +328,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "6.0.2" |     version: "6.0.2" | ||||||
|  |   pinput: | ||||||
|  |     dependency: "direct main" | ||||||
|  |     description: | ||||||
|  |       name: pinput | ||||||
|  |       sha256: "543da5bfdefd9e06914a12100f8c9156f84cef3efc14bca507c49e966c5b813b" | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "2.3.0" | ||||||
|   platform: |   platform: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -376,6 +389,14 @@ packages: | |||||||
|     description: flutter |     description: flutter | ||||||
|     source: sdk |     source: sdk | ||||||
|     version: "0.0.99" |     version: "0.0.99" | ||||||
|  |   smart_auth: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: smart_auth | ||||||
|  |       sha256: a25229b38c02f733d0a4e98d941b42bed91a976cb589e934895e60ccfa674cf6 | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.1.1" | ||||||
|   source_span: |   source_span: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
| @@ -464,6 +485,14 @@ packages: | |||||||
|       url: "https://pub.dev" |       url: "https://pub.dev" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "1.3.2" |     version: "1.3.2" | ||||||
|  |   universal_platform: | ||||||
|  |     dependency: transitive | ||||||
|  |     description: | ||||||
|  |       name: universal_platform | ||||||
|  |       sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc | ||||||
|  |       url: "https://pub.dev" | ||||||
|  |     source: hosted | ||||||
|  |     version: "1.0.0+1" | ||||||
|   uuid: |   uuid: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
|   | |||||||
| @@ -40,6 +40,8 @@ dependencies: | |||||||
|   cached_network_image: ^3.2.3 |   cached_network_image: ^3.2.3 | ||||||
|   remove_emoji_input_formatter: ^0.0.1+1 |   remove_emoji_input_formatter: ^0.0.1+1 | ||||||
|   provider: ^6.1.1 |   provider: ^6.1.1 | ||||||
|  |   pinput: ^2.2.21 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user