Compare commits

...

3 Commits

Author SHA1 Message Date
dacf5461f3 add registration password page 2024-02-06 14:40:10 +07:00
5d4bc47adf styling components 2024-02-06 14:39:16 +07:00
80e4657240 add string utils 2024-02-06 14:38:55 +07:00
10 changed files with 323 additions and 117 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

View File

@ -23,4 +23,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';
} }

View File

@ -55,78 +55,80 @@ 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(
width: width ?? _widthBtn, margin: EdgeInsets.symmetric(vertical: marginVertical ?? 24.0),
height: heightWrapContent ? null : height ?? _heightBtn, width: width ?? _widthBtn,
child: ElevatedButton( height: heightWrapContent ? null : height ?? _heightBtn,
style: ElevatedButton.styleFrom( child: ElevatedButton(
disabledBackgroundColor: isOutlined ? Colors.white : color.surface, style: ElevatedButton.styleFrom(
padding: contentPadding, disabledBackgroundColor: isOutlined ? Colors.white : color.surface,
backgroundColor: backgroundColor ?? padding: contentPadding,
(isOutlined backgroundColor: backgroundColor ??
? Colors.white (isOutlined
: isSecondaryColor ? Colors.white
? ColorPalette.grey : isSecondaryColor
: ColorPalette.primary), ? ColorPalette.grey
elevation: 0, : ColorPalette.primary),
shape: RoundedRectangleBorder( elevation: 0,
borderRadius: BorderRadius.circular(sizeBorderRadius ?? 48), shape: RoundedRectangleBorder(
side: isOutlined borderRadius: BorderRadius.circular(sizeBorderRadius ?? 48),
? BorderSide( side: isOutlined
color: disabled ? BorderSide(
? color.surface color: disabled
: isSecondaryColor ? color.surface
? ColorPalette.greyBorder : isSecondaryColor
: ColorPalette.primary, ? ColorPalette.greyBorder
) : ColorPalette.primary,
: BorderSide.none, )
: BorderSide.none,
),
), ),
), onPressed: disabled ? null : onPressed,
onPressed: disabled ? null : onPressed, child: Row(
child: Row( mainAxisAlignment: mainAxisAlignmentContent ??
mainAxisAlignment: mainAxisAlignmentContent ?? (prefixIcon != null
(prefixIcon != null ? MainAxisAlignment.center
? MainAxisAlignment.center : suffixIcon != null
: suffixIcon != null ? MainAxisAlignment.end
? MainAxisAlignment.end : MainAxisAlignment.center),
: MainAxisAlignment.center), children: [
children: [ if (prefixIcon != null) ...[
if (prefixIcon != null) ...[ prefixIcon!,
prefixIcon!, SizedBox(width: widthPrefix),
SizedBox(width: widthPrefix), ] else
] else Container(),
Container(), Flexible(
Flexible( child: Text(
child: Text( name,
name, textAlign: TextAlign.center,
textAlign: TextAlign.center, maxLines: maxLines,
maxLines: maxLines, overflow: TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis, style: TextStyle(
style: TextStyle( fontSize: textSize ?? 16,
fontSize: textSize ?? 16, fontWeight: textWeight,
fontWeight: textWeight, color: textColor ??
color: textColor ?? (disabled && isOutlined
(disabled && isOutlined ? color.primary
? color.primary : disabled
: disabled ? Colors.white
? Colors.white : isOutlined && isSecondaryColor
: isOutlined && isSecondaryColor ? ColorPalette.blackFont
? ColorPalette.blackFont : isOutlined
: isOutlined ? color.primary
? color.primary : isSecondaryColor
: isSecondaryColor ? Colors.white
? Colors.white : Colors.white),
: Colors.white), ),
), ),
), ),
), if (suffixIcon != null) ...[
if (suffixIcon != null) ...[ SizedBox(width: widthSuffix),
SizedBox(width: widthSuffix), suffixIcon!
suffixIcon! ] else
] else Container()
Container() ],
], ),
), ),
), ),
); );

View File

@ -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,6 +34,7 @@ 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,

View File

@ -94,12 +94,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,

View 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);
}
}

View File

@ -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_off_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);
},
)
],
),
),
);
}
}

View File

@ -2,7 +2,7 @@ import 'package:cims_apps/application/component/button/button_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/core/route/route.dart';
import 'package:cims_apps/features/auth/registration/view/initial_registration_step.dart'; import 'package:cims_apps/features/auth/registration/view/registration_password_view.dart';
import 'package:flutter/gestures.dart'; import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -29,7 +29,7 @@ class RegistrationView extends StatelessWidget {
ButtonView( ButtonView(
name: 'Next', name: 'Next',
onPressed: () { onPressed: () {
routePush(context, page: const InitialRegistrationStep()); routePush(context, page: const RegistrationPasswordView());
}, },
), ),
Align( Align(

View File

@ -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,53 +73,63 @@ 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(
appBar: AppBar( onWillPop: () async {
title: const Text('Registration'), await routePush(context,
), page: const BottomNavigationView(),
body: Stack( routeType: RouteType.pushReplace);
children: [ return false;
Consumer<SubmissionDataViewModel>( },
builder: (context, provider, child) { child: Scaffold(
return Column( appBar: AppBar(
crossAxisAlignment: CrossAxisAlignment.start, title: const Text('Registration'),
mainAxisAlignment: MainAxisAlignment.spaceBetween, ),
children: [ body: Stack(
Padding( children: [
padding: const EdgeInsets.symmetric( Consumer<SubmissionDataViewModel>(
horizontal: 16.0, vertical: 16.0), builder: (context, provider, child) {
child: Row( return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate( mainAxisAlignment: MainAxisAlignment.spaceBetween,
provider.stepAmount, children: [
(index) => _stepItem( Padding(
isCurrentStep: provider.currentStep == index + 1, padding: const EdgeInsets.symmetric(
horizontal: 16.0, vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(
provider.stepAmount,
(index) => _stepItem(
isCurrentStep:
provider.currentStep == index + 1,
),
), ),
), ),
), ),
), Expanded(
Expanded( child: Container(
child: Container( padding:
padding: const EdgeInsets.symmetric(horizontal: 16.0), const EdgeInsets.symmetric(horizontal: 16.0),
child: _content(provider.currentStep), child: _content(provider.currentStep),
),
), ),
), provider.currentStep == 3
provider.currentStep == 3 ? const SizedBox()
? const SizedBox() : Align(
: Align( alignment: Alignment.bottomCenter,
alignment: Alignment.bottomCenter, child: ButtonView(
child: ButtonView( name: 'Next',
name: 'Next', marginVertical: 16.0,
marginVertical: 16.0, onPressed: () {
onPressed: () { provider.nextSubmission(context);
provider.nextSubmission(context); },
}, ),
), )
) ],
], );
); }),
}), ],
], ),
), ),
); );
}); });

View File

@ -0,0 +1,19 @@
import 'package:flutter/material.dart';
class RegistrationViewModel extends ChangeNotifier {
TextEditingController passwordCtrl = TextEditingController();
TextEditingController confirmPasswordCtrl = TextEditingController();
var formKey = GlobalKey<FormState>();
bool showPassword = false;
bool showPasswordConfirm = false;
void toggleVisibility() {
showPassword = !showPassword;
notifyListeners();
}
void toggleVisibilityConfirm() {
showPasswordConfirm = !showPasswordConfirm;
notifyListeners();
}
}