diff --git a/lib/core/res/colors.dart b/lib/core/res/colors.dart index 689c761..ae03877 100644 --- a/lib/core/res/colors.dart +++ b/lib/core/res/colors.dart @@ -54,6 +54,11 @@ class C { //Speaker Card fonts static final Color cardFontColor = HexColor("#A3A3A3"); + ///Otp ring colors + static final Color firstRing = HexColor("#2DEBFF"); + static final Color secondRing = HexColor("#FB28E6"); + static final Color thirdRing = HexColor("#EFEA79"); + static final Color fourthRing = HexColor("#AE82FF"); //Sponsor Background static final Color sponsorPageBackground = HexColor("#372981"); diff --git a/lib/core/res/strings.dart b/lib/core/res/strings.dart index 9b59e3e..0eeafc2 100644 --- a/lib/core/res/strings.dart +++ b/lib/core/res/strings.dart @@ -145,6 +145,7 @@ class S { static const routeEvents = "/events"; static const routeSponsors = "/sponsors"; static const routeEsummit = "/esummit"; + static const routeForgotPassword = "/forgotPassword"; static const routeTeam = "/team"; static const routeAboutUs = "/about_us"; static const routeBQuiz = "/bquiz"; diff --git a/lib/main.dart b/lib/main.dart index 6e23e41..182dcea 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,6 @@ +import 'package:ecellapp/screens/forgot_password/cubit/forgot_password_cubit.dart'; +import 'package:ecellapp/screens/forgot_password/forgot_password.dart'; +import 'package:ecellapp/screens/forgot_password/forgot_password_repository.dart'; import 'package:ecellapp/screens/b_quiz/bquiz.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -63,6 +66,10 @@ class ECellApp extends StatelessWidget { S.routeSponsors: (_) => BlocProvider( create: (_) => SponsorsCubit(FakeSponsorsRepository()), child: SponsorsScreen()), S.routeEsummit: (_) => ESummitScreen(), + S.routeForgotPassword: (_) => BlocProvider( + create: (_) => ForgotPasswordCubit(FakeForgotPasswordRepository()), + child: ForgotPasswordScreen(), + ), S.routeBQuiz: (_) => BQuiz(), S.routeAboutUs: (_) => BlocProvider(create: (_) => TeamCubit(FakeTeamRepository()), child: AboutUsScreen()), diff --git a/lib/screens/forgot_password/ask_email.dart b/lib/screens/forgot_password/ask_email.dart new file mode 100644 index 0000000..1edacc0 --- /dev/null +++ b/lib/screens/forgot_password/ask_email.dart @@ -0,0 +1,179 @@ +import 'package:ecellapp/core/res/colors.dart'; +import 'package:ecellapp/core/res/dimens.dart'; +import 'package:ecellapp/core/res/strings.dart'; +import 'package:ecellapp/widgets/email_field.dart'; +import 'package:ecellapp/widgets/screen_background.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +class AskEmailScreen extends StatefulWidget { + AskEmailScreen({Key key, this.onSubmit, this.emailController}) : super(key: key); + + final Function onSubmit; + final TextEditingController emailController; + + @override + _AskEmailScreenState createState() => _AskEmailScreenState(); +} + +class _AskEmailScreenState extends State { + ScrollController scrollController = ScrollController(); + + @override + Widget build(BuildContext context) { + double width = MediaQuery.of(context).size.width; + double height = MediaQuery.of(context).size.height; + double bottom = MediaQuery.of(context).viewInsets.bottom; + double heightFactor = height / 1000; + if (scrollController.hasClients) { + if (bottom > height * 0.25) { + scrollController.animateTo( + bottom - height * 0.25, + duration: Duration(milliseconds: 300), + curve: Curves.ease, + ); + } else { + scrollController.animateTo(0, duration: Duration(milliseconds: 300), curve: Curves.ease); + } + } + return DefaultTextStyle( + style: GoogleFonts.roboto().copyWith(color: C.primaryUnHighlightedColor), + child: Stack( + children: [ + ScreenBackground(elementId: 1), + SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + controller: scrollController, + child: Container( + height: height * 1.25, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + flex: 5, + child: Container( + padding: EdgeInsets.fromLTRB(0, heightFactor * 100, 0, 0), + alignment: Alignment.center, + child: Text( + "Step 1/3", + style: + TextStyle(fontSize: 35 * heightFactor, fontWeight: FontWeight.w600), + ), + )), + Flexible( + flex: 7, + child: Column( + children: [ + // Logo + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(left: D.horizontalPadding + 1), + child: Image.asset( + S.assetEcellLogoWhite, + width: width * 0.25 * heightFactor, + ), + ), + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(left: D.horizontalPadding, top: 20), + child: Text( + "Welcome", + style: + TextStyle(fontSize: 35 * heightFactor, fontWeight: FontWeight.w600), + ), + ), + //Text Greeting + Container( + alignment: Alignment.centerLeft, + padding: EdgeInsets.only(left: D.horizontalPadding, top: 5), + child: RichText( + text: TextSpan( + children: [ + TextSpan( + text: "Forgot your ", + style: TextStyle( + color: C.primaryUnHighlightedColor, + fontWeight: FontWeight.w300)), + TextSpan( + text: "Password ", + style: TextStyle(color: C.primaryHighlightedColor), + ), + TextSpan( + text: "? \n", + style: TextStyle( + color: C.primaryUnHighlightedColor, + fontWeight: FontWeight.w300)), + TextSpan( + text: + "We got you covered.\nJust enter your registered email address.", + style: TextStyle( + color: C.primaryUnHighlightedColor, + fontWeight: FontWeight.w300)), + ], + style: TextStyle(fontSize: 25 * heightFactor), + ), + ), + ), + SizedBox(height: 23 * heightFactor), + Padding( + padding: EdgeInsets.symmetric(horizontal: D.horizontalPadding), + child: Column( + children: [ + EmailField(widget.emailController), + SizedBox(height: 30 * heightFactor), + ], + ), + ), + ], + ), + ), + //LoginButton + Expanded( + child: Container( + padding: EdgeInsets.only(right: D.horizontalPadding), + alignment: Alignment.topRight, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(30)), + boxShadow: [ + BoxShadow( + color: C.authButtonColor.withOpacity(0.2), + blurRadius: 10, + spreadRadius: 3, + offset: Offset(0, 12), + ) + ], + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(30)), + ), + color: C.authButtonColor, + onPressed: () { + widget.onSubmit(); + }, + child: Container( + height: 60, + width: 120, + alignment: Alignment.center, + child: Text( + "Submit", + style: TextStyle( + color: C.primaryUnHighlightedColor, fontSize: 20 * heightFactor), + ), + ), + ), + ), + ), + ), + //To flex background + Expanded(flex: 9, child: Container()), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/forgot_password/enter_otp.dart b/lib/screens/forgot_password/enter_otp.dart new file mode 100644 index 0000000..bb522e1 --- /dev/null +++ b/lib/screens/forgot_password/enter_otp.dart @@ -0,0 +1,297 @@ +import 'package:ecellapp/core/res/colors.dart'; +import 'package:ecellapp/widgets/screen_background.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'widgets/numeric_pad.dart'; + +class EnterOTPScreen extends StatelessWidget { + const EnterOTPScreen({Key key, this.otp, this.toVerify, this.numSelected}) : super(key: key); + + final Function toVerify; + final Function(int) numSelected; + final String otp; + + @override + Widget build(BuildContext context) { + final ScrollController scrollController = ScrollController(); + + double height = MediaQuery.of(context).size.height; + double bottom = MediaQuery.of(context).viewInsets.bottom; + double heightFactor = height / 1000; + if (scrollController.hasClients) { + if (bottom > height * 0.25) { + scrollController.animateTo( + bottom - height * 0.25, + duration: Duration(milliseconds: 300), + curve: Curves.ease, + ); + } else { + scrollController.animateTo(0, duration: Duration(milliseconds: 300), curve: Curves.ease); + } + } + print("OTPSCREEN: $otp"); + return DefaultTextStyle( + style: GoogleFonts.roboto().copyWith(color: C.primaryUnHighlightedColor), + child: Stack( + children: [ + //background + ScreenBackground(elementId: 0), + SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + controller: scrollController, + child: Container( + height: height * 1.00, + child: Column( + children: [ + // The text part od the screen + Flexible( + flex: 4, + child: Container( + padding: EdgeInsets.fromLTRB(0, heightFactor * 100, 0, 0), + alignment: Alignment.center, + child: Column( + children: [ + Expanded( + flex: 1, + child: Text( + "Step 2/3", + style: TextStyle( + fontSize: 40 * heightFactor, fontWeight: FontWeight.w600), + ), + ), + Expanded( + flex: 1, + child: Icon(Icons.verified_user, + color: Colors.white, size: 65 * heightFactor), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.center, + child: Text( + "Verify OTP", + style: TextStyle( + fontWeight: FontWeight.w300, + color: C.primaryHighlightedColor, + fontSize: heightFactor * 30, + ), + ), + ), + ), + Container( + padding: + EdgeInsets.fromLTRB(heightFactor * 10, 0, heightFactor * 10, 0), + alignment: Alignment.center, + child: Column( + children: [ + Text( + "An otp has been sent to the registered user", + style: TextStyle( + fontSize: heightFactor * 20, + ), + ), + Padding( + padding: EdgeInsets.only(top: 10 * heightFactor), + child: Text( + "email", + style: TextStyle( + fontSize: heightFactor * 20, + ), + )), + ], + ), + ), + ], + ), + ), + ), + Flexible( + flex: 3, + child: Column( + children: [ + //!OTP number fields + Padding( + padding: const EdgeInsets.only(top: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + otp[0], + style: TextStyle(fontSize: 23), + ), + ), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: C.firstRing, + width: 2.5, + ), + ), + ), + Container( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + otp[1], + style: TextStyle(fontSize: 23), + ), + ), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: C.secondRing, + width: 2.5, + ), + ), + ), + Container( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + otp[2], + style: TextStyle(fontSize: 23), + ), + ), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: C.thirdRing, + width: 2.5, + ), + ), + ), + Container( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + otp[3], + style: TextStyle(fontSize: 23), + ), + ), + decoration: BoxDecoration( + shape: BoxShape.circle, + border: Border.all( + color: C.fourthRing, + width: 2.5, + ), + ), + ), + ], + ), + ), + Center( + child: RichText( + text: TextSpan( + text: "You can request another OTP in ", + children: [ + TextSpan( + text: "[Time]s", + style: TextStyle( + color: C.primaryUnHighlightedColor, + fontWeight: FontWeight.w600, + )), + ], + style: TextStyle( + fontSize: 15 * heightFactor, fontWeight: FontWeight.w300), + ), + ), + ), + + SizedBox(height: 30 * heightFactor), + //!Verify button + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Container( + decoration: BoxDecoration( + border: Border.all( + color: Colors.white, + width: 2.0, + ), + borderRadius: BorderRadius.all(Radius.circular(30)), + boxShadow: [ + BoxShadow( + color: C.backgroundTop, + // blurRadius: 10, + //spreadRadius: 3, + //offset: Offset(0, 12), + ) + ], + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(30)), + ), + color: Colors.transparent, + onPressed: () => toVerify(), + child: Container( + height: 50, + width: 120, + alignment: Alignment.center, + child: Text( + "Resend", + style: TextStyle( + color: C.primaryUnHighlightedColor, + fontSize: 20 * heightFactor), + ), + ), + ), + ), + SizedBox( + width: 20 * heightFactor, + ), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(30)), + boxShadow: [ + BoxShadow( + color: C.authButtonColor.withOpacity(0.2), + blurRadius: 10, + spreadRadius: 3, + offset: Offset(0, 12), + ) + ], + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(30)), + ), + color: C.authButtonColor, + onPressed: () => toVerify(), + child: Container( + height: 54, + width: 124, + alignment: Alignment.center, + child: Text( + "Submit", + style: TextStyle( + color: C.primaryUnHighlightedColor, + fontSize: 20 * heightFactor), + ), + ), + ), + ), + ], + ), + ], + ), + ), + Flexible( + flex: 4, + child: NumericPad(onNumberSelected: numSelected), + ), + SizedBox(height: heightFactor * 40) + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/forgot_password/forgot_password.dart b/lib/screens/forgot_password/forgot_password.dart index 2c63f4d..7e46ad5 100644 --- a/lib/screens/forgot_password/forgot_password.dart +++ b/lib/screens/forgot_password/forgot_password.dart @@ -1,82 +1,96 @@ +import 'package:ecellapp/core/res/colors.dart'; +import 'package:ecellapp/core/res/dimens.dart'; +import 'package:ecellapp/core/res/strings.dart'; +import 'package:ecellapp/screens/forgot_password/ask_email.dart'; import 'package:ecellapp/screens/forgot_password/cubit/forgot_password_cubit.dart'; -import 'package:ecellapp/screens/forgot_password/widgets/otp_field.dart'; +import 'package:ecellapp/screens/forgot_password/enter_otp.dart'; import 'package:ecellapp/widgets/ecell_animation.dart'; -import 'package:ecellapp/widgets/email_field.dart'; -import 'package:ecellapp/widgets/password_field.dart'; +import 'package:ecellapp/widgets/screen_background.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -class ForgotPasswordScreen extends StatelessWidget { - final TextEditingController emailController = TextEditingController(); - final TextEditingController otpController = TextEditingController(); +import 'reset_password.dart'; + +class ForgotPasswordScreen extends StatefulWidget { + @override + _ForgotPasswordScreenState createState() => _ForgotPasswordScreenState(); +} + +class _ForgotPasswordScreenState extends State { final TextEditingController passwordController = TextEditingController(); final TextEditingController confirmPasswordController = TextEditingController(); + final TextEditingController emailController = TextEditingController(); + final TextEditingController otpController = TextEditingController(); + + String otpEntered = " " * 4, finalOTP = ""; @override Widget build(BuildContext context) { return Scaffold( - body: BlocConsumer( - listener: (context, state) { - if (state is ForgotPasswordError) { - Scaffold.of(context).showSnackBar( - SnackBar(content: Text(state.message)), - ); - } - }, - builder: (context, state) { - return Stack( - children: [ - if (state is ForgotEmailInitial) - _initialForgotPassword(context, state) - else if (state is ForgotLoading) - _buildLoading(context) - else if (state is ForgotOTPInitial) - _enterOTP(context, state) + extendBodyBehindAppBar: true, + appBar: AppBar( + elevation: 0, + backgroundColor: Colors.transparent, + leading: Container( + padding: EdgeInsets.only(left: D.horizontalPadding - 10, top: 10), + child: IconButton( + icon: Icon(Icons.arrow_back_ios, color: Colors.white, size: 30), + onPressed: () => Navigator.of(context).pop(), + ), + ), + ), + backgroundColor: Colors.transparent, + body: Stack( + children: [ + ScreenBackground(elementId: 0), + BlocConsumer( + listener: (context, state) { + if (state is ForgotPasswordError) { + Scaffold.of(context).showSnackBar( + SnackBar(content: Text(state.message)), + ); + } + }, + builder: (context, state) { + if (state is ForgotLoading) + return _buildLoading(context); else if (state is ForgotPasswordError) - _uiUpdateForNetworkError(context, state.state) - else if (state is ForgotResetInitial) - _resetPassword(context, state) + return _stepScreen(context, state.state); else if (state is ForgotResetSuccess) - _passwordResetSuccess() + return _passwordResetSuccess(); else - _initialForgotPassword(context, state) - ], - ); - }, + return _stepScreen(context, state); + }, + ), + ], ), ); } - Widget _uiUpdateForNetworkError(BuildContext context, ForgotPasswordState state) { - if (state is ForgotEmailInitial) { - return _initialForgotPassword(context, state); - } else if (state is ForgotOTPInitial) { - return _enterOTP(context, state); - } else if (state is ForgotPasswordError) { - return _enterOTP(context, state); - } else if (state is ForgotResetInitial) { - return _resetPassword(context, state); - } else { - return _initialForgotPassword(context, state); - } - } - - Widget _initialForgotPassword(BuildContext context, ForgotPasswordState state) { - return Padding( - padding: const EdgeInsets.all(40.0), - child: Column( - children: [ - Text("Enter email"), - EmailField(emailController), - FlatButton( - onPressed: () { - _sendOTP(context, state); - }, - child: Text("Press me")), - ], - ), - ); + Widget _stepScreen(BuildContext context, ForgotPasswordState state) { + if (state is ForgotEmailInitial) + return AskEmailScreen( + emailController: emailController, + onSubmit: () => _sendOTP(context, state), + ); + else if (state is ForgotOTPInitial) + return EnterOTPScreen( + numSelected: _onNumSelected, + otp: otpEntered, + toVerify: () => _verifyOtp(context, state), + ); + else if (state is ForgotResetInitial) + return ResetPassword( + onConfirm: () => _changePassword(context, state), + confirmPasswordController: confirmPasswordController, + passwordController: passwordController, + ); + else + return AskEmailScreen( + emailController: emailController, + onSubmit: () => _sendOTP(context, state), + ); } Widget _buildLoading(BuildContext context) { @@ -84,42 +98,32 @@ class ForgotPasswordScreen extends StatelessWidget { return Center(child: ECellLogoAnimation(size: width / 2)); } - Widget _enterOTP(BuildContext context, ForgotPasswordState state) { - return Padding( - padding: const EdgeInsets.all(40.0), - child: Column( - children: [ - Text("Enter otp"), - OTPField(otpController), - FlatButton( - onPressed: () { - _verifyOtp(context, state); - }, - child: Text("Press me")), - ], - ), - ); - } - Widget _passwordResetSuccess() { - return Padding( - padding: const EdgeInsets.all(40.0), - child: Text("success"), - ); - } - - Widget _resetPassword(BuildContext context, ForgotPasswordState state) { - return Padding( - padding: const EdgeInsets.all(40.0), + return Center( child: Column( + mainAxisAlignment: MainAxisAlignment.center, children: [ - PasswordField(passwordController), - PasswordField(confirmPasswordController), - FlatButton( - onPressed: () { - _changePassword(context, state); - }, - child: Text("change password")), + Image.asset( + S.assetEcellLogoWhite, + height: 170, + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 120), + child: Row( + children: [ + Text( + "Sucess", + style: TextStyle( + fontSize: 40, fontWeight: FontWeight.w600, color: C.primaryHighlightedColor), + ), + Icon( + Icons.check_circle, + color: C.primaryUnHighlightedColor, + size: 40, + ) + ], + ), + ) ], ), ); @@ -127,17 +131,40 @@ class ForgotPasswordScreen extends StatelessWidget { void _sendOTP(BuildContext context, ForgotPasswordState state) { final cubit = context.read(); - cubit.sendOTP(emailController.text, state); + bool emailValid = RegExp(r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+") + .hasMatch(emailController.text); + if (emailValid) { + cubit.sendOTP(emailController.text, state); + } } void _verifyOtp(BuildContext context, ForgotPasswordState state) { final cubit = context.read(); - cubit.checkOTP(otpController.text, state, emailController.text); + + cubit.checkOTP(otpEntered.substring(0, 4), state, emailController.text); } void _changePassword(BuildContext context, ForgotPasswordState state) { final cubit = context.read(); - cubit.changePassword(emailController.text, otpController.text, passwordController.text, + + cubit.changePassword(emailController.text, otpEntered.substring(0, 4), passwordController.text, confirmPasswordController.text, state); } + + _onNumSelected(int numEntered) { + int length = otpEntered.length; + setState(() { + if (numEntered == -1) { + if (length > 0) { + finalOTP = finalOTP.substring(0, finalOTP.length - 1); + otpEntered = finalOTP + " " * (4 - finalOTP.length); + } + } else if (length < 5) { + if (finalOTP.length < 4) { + finalOTP += numEntered.toString(); + } + otpEntered = finalOTP + " " * (4 - finalOTP.length); + } + }); + } } diff --git a/lib/screens/forgot_password/forgot_password_repository.dart b/lib/screens/forgot_password/forgot_password_repository.dart index ee6ea9a..08bb54a 100644 --- a/lib/screens/forgot_password/forgot_password_repository.dart +++ b/lib/screens/forgot_password/forgot_password_repository.dart @@ -18,7 +18,7 @@ class FakeForgotPasswordRepository extends ForgotPasswordRepository { @override // this is to simulate a delay for getting otp Future sendOTP(String email) async { - await Future.delayed(Duration(seconds: 2)); + await Future.delayed(Duration(seconds: 1)); if (Random().nextBool()) { return; } else { @@ -33,7 +33,7 @@ class FakeForgotPasswordRepository extends ForgotPasswordRepository { // this to simulate the process of otp verification @override Future checkOTP(String otp, String email) async { - await Future.delayed(Duration(seconds: 2)); + await Future.delayed(Duration(seconds: 1)); if (Random().nextBool()) { if (otp == "1234") { return; @@ -48,7 +48,7 @@ class FakeForgotPasswordRepository extends ForgotPasswordRepository { // this is to simulate the process of change in email @override Future changePassword(String email, String otp, String password) async { - await Future.delayed(Duration(seconds: 2)); + await Future.delayed(Duration(seconds: 1)); if (Random().nextBool()) { return; } else { diff --git a/lib/screens/forgot_password/reset_password.dart b/lib/screens/forgot_password/reset_password.dart new file mode 100644 index 0000000..ad51975 --- /dev/null +++ b/lib/screens/forgot_password/reset_password.dart @@ -0,0 +1,161 @@ +import 'package:ecellapp/core/res/colors.dart'; +import 'package:ecellapp/core/res/dimens.dart'; +import 'package:ecellapp/widgets/password_field.dart'; +import 'package:ecellapp/widgets/screen_background.dart'; +import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; + +import 'widgets/confirm_password.dart'; + +class ResetPassword extends StatelessWidget { + const ResetPassword({ + Key key, + this.onConfirm, + this.passwordController, + this.confirmPasswordController, + }) : super(key: key); + final Function onConfirm; + final TextEditingController passwordController; + final TextEditingController confirmPasswordController; + @override + Widget build(BuildContext context) { + final ScrollController scrollController = ScrollController(); + + double height = MediaQuery.of(context).size.height; + double bottom = MediaQuery.of(context).viewInsets.bottom; + double heightFactor = height / 1000; + if (scrollController.hasClients) { + if (bottom > height * 0.5) { + scrollController.animateTo( + bottom - height * 0.5, + duration: Duration(milliseconds: 300), + curve: Curves.ease, + ); + } else { + scrollController.animateTo(0, duration: Duration(milliseconds: 300), curve: Curves.ease); + } + } + return DefaultTextStyle( + style: GoogleFonts.roboto().copyWith(color: C.primaryUnHighlightedColor), + child: Stack( + children: [ + //background + ScreenBackground(elementId: 0), + SingleChildScrollView( + physics: NeverScrollableScrollPhysics(), + controller: scrollController, + child: Container( + height: height * 1.25, + child: Column( + children: [ + //3/3 text + Expanded( + flex: 1, + child: Container( + padding: EdgeInsets.fromLTRB(0, heightFactor * 100, 0, 0), + alignment: Alignment.center, + child: Column( + children: [ + Expanded( + flex: 1, + child: Text( + "Step 3/3", + style: TextStyle( + fontSize: 35 * heightFactor, fontWeight: FontWeight.w600), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.center, + child: Text( + "Reset Password", + style: TextStyle( + color: C.primaryHighlightedColor, + fontSize: heightFactor * 30, + ), + ), + ), + ), + Expanded( + flex: 1, + child: Container( + alignment: Alignment.center, + child: Text( + "Please enter your new password", + style: TextStyle( + fontSize: heightFactor * 25, + ), + ), + ), + ), + ], + ), + ), + ), + // password and confirm password fields + SizedBox( + height: 50, + ), + Expanded( + flex: 3, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: Column( + children: [ + PasswordField(passwordController), + SizedBox(height: 20 * heightFactor), + ConfirmPasswordField(confirmPasswordController), + ], + ), + ), + SizedBox(height: 20 * heightFactor), + Container( + padding: EdgeInsets.only(right: D.horizontalPadding), + alignment: Alignment.topRight, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(30)), + boxShadow: [ + BoxShadow( + color: C.authButtonColor.withOpacity(0.2), + blurRadius: 10, + spreadRadius: 3, + offset: Offset(0, 12), + ) + ], + ), + child: RaisedButton( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(30)), + ), + color: C.authButtonColor, + onPressed: () => onConfirm(), + child: Container( + height: 60, + width: 100, + alignment: Alignment.center, + child: Text( + "Reset", + style: TextStyle( + color: C.primaryUnHighlightedColor, + fontSize: 20 * heightFactor), + ), + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/screens/forgot_password/widgets/confirm_password.dart b/lib/screens/forgot_password/widgets/confirm_password.dart new file mode 100644 index 0000000..001a6c0 --- /dev/null +++ b/lib/screens/forgot_password/widgets/confirm_password.dart @@ -0,0 +1,53 @@ +import 'package:flutter/material.dart'; +import 'package:ecellapp/core/res/colors.dart'; +import 'package:ecellapp/core/res/dimens.dart'; + +class ConfirmPasswordField extends StatefulWidget { + const ConfirmPasswordField(this.controller); + + final TextEditingController controller; + + @override + _ConfirmPasswordFieldState createState() => _ConfirmPasswordFieldState(); +} + +class _ConfirmPasswordFieldState extends State { + bool _passwordVisible = false; + + @override + Widget build(BuildContext context) { + double height = MediaQuery.of(context).size.height; + double heightFactor = height >= 1000 ? 1 : height / 1000; + return TextFormField( + controller: widget.controller, + validator: _validator, + style: TextStyle( + color: C.primaryUnHighlightedColor, + fontSize: D.inputFieldFontSize * heightFactor, + ), + textInputAction: TextInputAction.next, + onEditingComplete: () => FocusScope.of(context)..nextFocus()..nextFocus(), + obscureText: !_passwordVisible, + decoration: InputDecoration( + errorStyle: TextStyle(fontSize: 0.1), + prefixIcon: Icon( + Icons.lock_outline, + size: D.iconSize * heightFactor, + color: C.primaryHighlightedColor, + ), + suffixIcon: IconButton( + icon: IconTheme( + child: _passwordVisible ? Icon(Icons.visibility) : Icon(Icons.visibility_off), + data: IconThemeData(color: C.primaryHighlightedColor, size: D.iconSize * heightFactor), + ), + onPressed: _togglePasswordVisibility, + ), + labelText: "Confirm Password", + ), + ); + } + + String _validator(String password) => password.isEmpty ? "" : null; + + void _togglePasswordVisibility() => setState(() => _passwordVisible = !_passwordVisible); +} diff --git a/lib/screens/forgot_password/widgets/numeric_pad.dart b/lib/screens/forgot_password/widgets/numeric_pad.dart new file mode 100644 index 0000000..360f874 --- /dev/null +++ b/lib/screens/forgot_password/widgets/numeric_pad.dart @@ -0,0 +1,135 @@ +import 'package:ecellapp/core/res/colors.dart'; +import 'package:flutter/material.dart'; + +class NumericPad extends StatelessWidget { + final Function(int) onNumberSelected; + + NumericPad({@required this.onNumberSelected}); + + @override + Widget build(BuildContext context) { + return Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 80), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildNumber(1), + buildNumber(2), + buildNumber(3), + ], + ), + ), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildNumber(4), + buildNumber(5), + buildNumber(6), + ], + ), + ), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildNumber(7), + buildNumber(8), + buildNumber(9), + ], + ), + ), + Container( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + buildEmptySpace(), + buildNumber(0), + buildBackspace(), + ], + ), + ), + ], + ), + ), + ); + } + + Widget buildNumber(int number) { + return Expanded( + child: GestureDetector( + onTap: () { + onNumberSelected(number); + }, + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 12.75), + child: Container( + height: 40, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.35), + borderRadius: BorderRadius.all( + Radius.circular(15), + ), + ), + child: Center( + child: Text( + number.toString(), + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + color: C.primaryUnHighlightedColor, + ), + ), + ), + ), + ), + ), + ); + } + + Widget buildBackspace() { + return Expanded( + child: GestureDetector( + onTap: () { + onNumberSelected(-1); + }, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.75), + child: Container( + height: 40, + decoration: BoxDecoration( + color: Colors.white.withOpacity(0.25), + borderRadius: BorderRadius.all( + Radius.circular(15), + ), + ), + child: Center( + child: Icon( + Icons.backspace_outlined, + size: 20, + color: Colors.white.withOpacity(0.75), + ), + ), + ), + ), + ), + ); + } + + Widget buildEmptySpace() { + return Expanded( + child: Container(), + ); + } +} diff --git a/lib/screens/forgot_password/widgets/otp_field.dart b/lib/screens/forgot_password/widgets/otp_field.dart deleted file mode 100644 index d6ddbb8..0000000 --- a/lib/screens/forgot_password/widgets/otp_field.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -class OTPField extends StatelessWidget { - const OTPField(this.controller); - - final TextEditingController controller; - - @override - Widget build(BuildContext context) { - return TextFormField( - controller: controller, - keyboardType: TextInputType.phone, - validator: _validateOTP, - decoration: InputDecoration( - suffixText: '*', - suffixStyle: TextStyle(color: Colors.red, fontSize: 20), - prefixIcon: Icon(Icons.error), - border: OutlineInputBorder(), - labelText: "OTP", - ), - ); - } - - String _validateOTP(String otp) { - if (otp.length != 4) { - return "Please enter 4 digit OTP"; - } else { - return null; - } - } -} diff --git a/lib/screens/home/tabs/profile.dart b/lib/screens/home/tabs/profile.dart index a34671b..3fb8d9c 100644 --- a/lib/screens/home/tabs/profile.dart +++ b/lib/screens/home/tabs/profile.dart @@ -64,7 +64,9 @@ class _ProfileScreenState extends State { Container( alignment: Alignment.topRight, child: GestureDetector( - onTap: () {}, //TODO + onTap: () { + Navigator.pushReplacementNamed(context, S.routeForgotPassword); + }, child: Text( "Change Password?", style: TextStyle( diff --git a/lib/screens/login/login.dart b/lib/screens/login/login.dart index a46381a..b7691cc 100644 --- a/lib/screens/login/login.dart +++ b/lib/screens/login/login.dart @@ -141,7 +141,7 @@ class LoginScreen extends StatelessWidget { TextStyle(fontSize: 20 * heightFactor, color: C.secondaryColor), ), onTap: () { - //TODO: Forgot Password Route + Navigator.pushNamed(context, S.routeForgotPassword); }, ), )