Compare commits

...

2 Commits

Author SHA1 Message Date
e538fa5927 fix: sign up feature 2024-02-06 19:21:41 +07:00
7ec266cded fix: otp component 2024-02-06 18:38:34 +07:00
8 changed files with 198 additions and 108 deletions

BIN
assets/icons/icon-flag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -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';

View File

@ -1,4 +1,3 @@
import 'package:cims_apps/application/component/button/button_view.dart';
import 'package:cims_apps/application/component/otp/otp_viewmodel.dart'; 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/component/text_caption/text_caption.dart';
import 'package:cims_apps/application/theme/color_palette.dart'; import 'package:cims_apps/application/theme/color_palette.dart';
@ -61,26 +60,48 @@ class OtpView extends StatelessWidget {
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
), ),
), ),
), onCompleted: (pin) => provider.enableButton(),
ButtonView( onChanged: (value) {
name: 'Verify', if (provider.ctrlPin.length != 4) {
// disabled: !provider.buttonIsActive, provider.enableButton(isActive: false);
// backgroundColor: ColorPalette.grey,
onPressed: () {
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();
}
});
} }
}, },
), ),
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,
),
),
),
),
], ],
)); ));
} }
@ -106,7 +127,9 @@ class OtpView extends StatelessWidget {
), ),
_otpContent(context, provider), _otpContent(context, provider),
TextButton( TextButton(
onPressed: () {}, onPressed: () {
provider.ctrlPin.clear();
},
child: const Text( child: const Text(
'Resend Code', 'Resend Code',
style: TextStyle( style: TextStyle(

View File

@ -14,4 +14,9 @@ class OtpViewModel extends ChangeNotifier {
} }
return false; return false;
} }
void enableButton({bool isActive = true}) {
buttonIsActive = isActive;
notifyListeners();
}
} }

View File

@ -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
@ -148,46 +149,47 @@ class TextFormView extends StatelessWidget {
enableInteractiveSelection: enableInteractiveSelection, enableInteractiveSelection: enableInteractiveSelection,
textAlign: isTextAlignCenter ? TextAlign.center : TextAlign.left, textAlign: isTextAlignCenter ? TextAlign.center : TextAlign.left,
decoration: InputDecoration( decoration: InputDecoration(
helperText: helperText, helperText: helperText,
errorStyle: errorStyle, errorStyle: errorStyle,
errorText: errorText, errorText: errorText,
errorMaxLines: 2, errorMaxLines: 2,
hintStyle: hintTextStyle ?? hintStyle: hintTextStyle ??
const TextStyle( const TextStyle(
fontSize: 14, fontSize: 14,
color: ColorPalette.greyFont, color: ColorPalette.greyFont,
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
),
isDense: true,
hintText: hintText,
filled: true,
fillColor: enabled && disableColor == false
? Colors.white
: const Color.fromARGB(255, 233, 236, 239),
disabledBorder: OutlineInputBorder(
borderRadius: _borderRadius,
borderSide: BorderSide(
color: disabledborderColor ?? ColorPalette.greyBorder,
), ),
isDense: true,
hintText: hintText,
filled: true,
fillColor: enabled && disableColor == false
? Colors.white
: const Color.fromARGB(255, 233, 236, 239),
disabledBorder: OutlineInputBorder(
borderRadius: _borderRadius,
borderSide: BorderSide(
color: disabledborderColor ?? ColorPalette.greyBorder,
), ),
), enabledBorder: OutlineInputBorder(
enabledBorder: OutlineInputBorder( borderRadius: _borderRadius,
borderRadius: _borderRadius, borderSide: BorderSide(
borderSide: BorderSide( color: enabledborderColor ?? ColorPalette.greyBorder,
color: enabledborderColor ?? ColorPalette.greyBorder, ),
), ),
), focusedBorder: OutlineInputBorder(
focusedBorder: OutlineInputBorder( borderRadius: _borderRadius,
borderRadius: _borderRadius, borderSide: BorderSide(
borderSide: BorderSide( color: focusedBorderColor ?? ColorPalette.greyBorder,
color: focusedBorderColor ?? ColorPalette.greyBorder, ),
), ),
), border: OutlineInputBorder(borderRadius: _borderRadius),
border: OutlineInputBorder(borderRadius: _borderRadius), suffixIcon: suffixIcon,
suffixIcon: suffixIcon, prefixIcon: prefixIcon,
prefixIcon: prefixIcon, suffixIconConstraints: suffixIconConstraints,
suffixIconConstraints: suffixIconConstraints, prefixIconConstraints: preffixIconConstraints,
prefixIconConstraints: preffixIconConstraints, prefix: prefix,
), contentPadding: EdgeInsets.zero),
) )
], ],
); );

View File

@ -55,7 +55,7 @@ class RegistrationPasswordView extends StatelessWidget {
}, },
child: Icon( child: Icon(
provider.showPassword provider.showPassword
? Icons.visibility_off_outlined ? Icons.visibility_outlined
: Icons.visibility_off_outlined, : Icons.visibility_off_outlined,
color: ColorPalette.greyDarker, color: ColorPalette.greyDarker,
), ),

View File

@ -1,9 +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/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/application/theme/color_palette.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';
@ -11,7 +18,7 @@ class RegistrationView extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
_showOtpWidget() { showOtpWidget() {
Navigator.of(context).pop(); Navigator.of(context).pop();
showModalBottomSheet( showModalBottomSheet(
context: context, context: context,
@ -35,56 +42,106 @@ class RegistrationView extends StatelessWidget {
); );
} }
return Scaffold( return ChangeNotifierProvider(
appBar: AppBar( create: (context) => RegistrationViewModel(),
title: const Text('Sign Up'), builder: (context, child) {
), return Scaffold(
body: Container( appBar: AppBar(
padding: const EdgeInsets.all(24.0), title: const Text('Sign Up'),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TextCaption(
title: 'Enter your phone number',
subtitle: 'Input your registered phone number',
), ),
TextFormView(name: 'Phone Number'), body: Container(
ButtonView( padding: const EdgeInsets.all(24.0),
name: 'Next', child: Consumer<RegistrationViewModel>(
onPressed: () { builder: (context, provider, child) {
_showOtpWidget(); return Form(
key: provider.formKeyPhone,
// routePush(context, page: const RegistrationPasswordView()); child: Column(
}, crossAxisAlignment: CrossAxisAlignment.start,
children: [
const TextCaption(
title: 'Enter your phone number',
subtitle: 'Input your registered 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(
name: 'Next',
onPressed: () {
if (provider.formKeyPhone.currentState!.validate()) {
showOtpWidget();
}
},
),
Align(
alignment: Alignment.center,
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(children: [
const TextSpan(
text: 'Already have an account? ',
style: TextStyle(
color: Colors.black,
decoration: TextDecoration.none,
),
),
TextSpan(
recognizer: TapGestureRecognizer()..onTap = () {},
text: ' Sign In',
style: const TextStyle(
color: Colors.blue,
),
),
]),
),
)
],
),
);
}),
), ),
Align( );
alignment: Alignment.center, });
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(children: [
const TextSpan(
text: 'Already have an account? ',
style: TextStyle(
color: Colors.black,
decoration: TextDecoration.none,
),
),
TextSpan(
recognizer: TapGestureRecognizer()
..onTap = () {
print('object');
},
text: ' Sign In',
style: const TextStyle(
color: Colors.blue,
),
),
]),
),
)
],
),
),
);
} }
} }

View File

@ -3,7 +3,9 @@ import 'package:flutter/material.dart';
class RegistrationViewModel extends ChangeNotifier { class RegistrationViewModel extends ChangeNotifier {
TextEditingController passwordCtrl = TextEditingController(); TextEditingController passwordCtrl = TextEditingController();
TextEditingController confirmPasswordCtrl = TextEditingController(); TextEditingController confirmPasswordCtrl = TextEditingController();
TextEditingController phoneNumberCtrl = TextEditingController();
var formKey = GlobalKey<FormState>(); var formKey = GlobalKey<FormState>();
var formKeyPhone = GlobalKey<FormState>();
bool showPassword = false; bool showPassword = false;
bool showPasswordConfirm = false; bool showPasswordConfirm = false;