@@ -5,6 +5,7 @@ import 'package:firebase_core/firebase_core.dart';
55import 'package:flutter/cupertino.dart' ;
66import 'package:flutter/foundation.dart' ;
77import 'package:flutter/material.dart' ;
8+ import 'package:flutter/services.dart' ;
89import 'package:google_fonts/google_fonts.dart' ;
910
1011// https://github.com/FilledStacks/responsive_builder/blob/master/lib/src/sizing_information.dart#L85
@@ -122,6 +123,13 @@ class _LoginPageState extends State<LoginPage> {
122123 child: Column (
123124 crossAxisAlignment: CrossAxisAlignment .center,
124125 children: [
126+ Text (
127+ 'Sign In' ,
128+ style: Theme .of (context).textTheme.headline6? .copyWith (
129+ color: Colors .white,
130+ ),
131+ ),
132+ SizedBox (height: 16 ),
125133 TextFormField (
126134 controller: controllerUsername,
127135 decoration: InputDecoration (
@@ -139,16 +147,7 @@ class _LoginPageState extends State<LoginPage> {
139147 color: Colors .white,
140148 ),
141149 keyboardType: TextInputType .emailAddress,
142- validator: (value) {
143- if (value == null || value.isEmpty) {
144- return 'Enter an email address' ;
145- } else {
146- final isEmailValid = RegExp (
147- r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+" ,
148- ).hasMatch (value);
149- return isEmailValid ? null : 'Invalid email' ;
150- }
151- },
150+ validator: emailValidator,
152151 textInputAction: TextInputAction .next,
153152 ),
154153 SizedBox (height: 16 ),
@@ -312,7 +311,12 @@ class _LoginPageState extends State<LoginPage> {
312311 children: [
313312 InkWell (
314313 onTap: () {
315- // TODO: fitur forgot password
314+ Navigator .push (
315+ context,
316+ MaterialPageRoute (
317+ builder: (context) => ForgotPasswordPage (),
318+ ),
319+ );
316320 },
317321 child: Focus (
318322 focusNode: focusNodeForgotPassword,
@@ -328,14 +332,6 @@ class _LoginPageState extends State<LoginPage> {
328332 ],
329333 );
330334 }
331-
332- InputBorder _createUnderlineInputBorder () {
333- return UnderlineInputBorder (
334- borderSide: BorderSide (
335- color: Colors .white,
336- ),
337- );
338- }
339335}
340336
341337class HomePage extends StatelessWidget {
@@ -477,3 +473,187 @@ void _showSnackBar(BuildContext context, String message, double widthScreen) {
477473 );
478474 }
479475}
476+
477+ InputBorder _createUnderlineInputBorder () {
478+ return UnderlineInputBorder (
479+ borderSide: BorderSide (
480+ color: Colors .white,
481+ ),
482+ );
483+ }
484+
485+ class ForgotPasswordPage extends StatefulWidget {
486+ @override
487+ _ForgotPasswordPageState createState () => _ForgotPasswordPageState ();
488+ }
489+
490+ class _ForgotPasswordPageState extends State <ForgotPasswordPage > {
491+ final formState = GlobalKey <FormState >();
492+ final controllerEmail = TextEditingController ();
493+ final firebaseAuth = FirebaseAuth .instance;
494+ final focusNodeLabelForgotPassword = FocusNode ();
495+ final focusNodeLabelResetPasswordCode = FocusNode ();
496+
497+ var widthScreen = 0.0 ;
498+ var isLoading = false ;
499+
500+ @override
501+ Widget build (BuildContext context) {
502+ final mediaQueryData = MediaQuery .of (context);
503+ widthScreen = mediaQueryData.size.width;
504+ return Scaffold (
505+ body: Stack (
506+ children: [
507+ _buildWidgetImageBackground (),
508+ _buildWidgetOverlayImageBackground (),
509+ _buildWidgetContent (),
510+ ],
511+ ),
512+ );
513+ }
514+
515+ Widget _buildWidgetContent () {
516+ return Center (
517+ child: Container (
518+ padding: EdgeInsets .symmetric (horizontal: 16 ),
519+ width: widthScreen > _mobileExtraLarge ? _mobileExtraLarge : double .infinity,
520+ child: Column (
521+ mainAxisAlignment: MainAxisAlignment .center,
522+ children: [
523+ _buildWidgetTitleApp (context),
524+ SizedBox (height: 48 ),
525+ _buildWidgetForm (),
526+ ],
527+ ),
528+ ),
529+ );
530+ }
531+
532+ Widget _buildWidgetForm () {
533+ return IgnorePointer (
534+ ignoring: isLoading,
535+ child: _buildWidgetFormResetPassword (),
536+ );
537+ }
538+
539+ Widget _buildWidgetFormResetPassword () {
540+ return Form (
541+ key: formState,
542+ child: Column (
543+ crossAxisAlignment: CrossAxisAlignment .center,
544+ children: [
545+ Focus (
546+ focusNode: focusNodeLabelForgotPassword,
547+ child: Text (
548+ 'Forgot Password' ,
549+ style: Theme .of (context).textTheme.headline6? .copyWith (
550+ color: Colors .white,
551+ ),
552+ ),
553+ ),
554+ SizedBox (height: 16 ),
555+ TextFormField (
556+ controller: controllerEmail,
557+ decoration: InputDecoration (
558+ hintText: 'Email' ,
559+ hintStyle: TextStyle (
560+ color: Colors .grey[500 ],
561+ ),
562+ enabledBorder: _createUnderlineInputBorder (),
563+ icon: Icon (
564+ CupertinoIcons .mail,
565+ color: Colors .white,
566+ ),
567+ ),
568+ style: TextStyle (
569+ color: Colors .white,
570+ ),
571+ keyboardType: TextInputType .emailAddress,
572+ validator: emailValidator,
573+ textInputAction: TextInputAction .next,
574+ onFieldSubmitted: (_) {
575+ _doResetPasswordByEmail ();
576+ },
577+ ),
578+ SizedBox (height: 24 ),
579+ _buildWidgetButtonResetPassword (),
580+ SizedBox (height: 24 ),
581+ TextButton (
582+ onPressed: () {
583+ Navigator .pop (context);
584+ },
585+ child: Text ('Back to signin' ),
586+ ),
587+ ],
588+ ),
589+ );
590+ }
591+
592+ Widget _buildWidgetButtonResetPassword () {
593+ Widget ? widgetLoading;
594+ if (isLoading) {
595+ if (kIsWeb) {
596+ widgetLoading = SizedBox (
597+ width: 20 ,
598+ height: 20 ,
599+ child: CircularProgressIndicator (
600+ valueColor: AlwaysStoppedAnimation <Color >(
601+ Colors .white,
602+ ),
603+ strokeWidth: 2 ,
604+ ),
605+ );
606+ } else {
607+ widgetLoading = Platform .isIOS || Platform .isMacOS
608+ ? CupertinoActivityIndicator ()
609+ : SizedBox (
610+ width: 20 ,
611+ height: 20 ,
612+ child: CircularProgressIndicator (
613+ valueColor: AlwaysStoppedAnimation <Color >(
614+ Colors .white,
615+ ),
616+ strokeWidth: 2 ,
617+ ),
618+ );
619+ }
620+ }
621+ final padding = _setPaddingButton ();
622+ return ElevatedButton (
623+ onPressed: () {
624+ _doResetPasswordByEmail ();
625+ },
626+ child: widgetLoading ?? Text ('RESET PASSWORD' ),
627+ style: ElevatedButton .styleFrom (
628+ padding: padding,
629+ ),
630+ );
631+ }
632+
633+ void _doResetPasswordByEmail () async {
634+ if (formState.currentState! .validate ()) {
635+ focusNodeLabelForgotPassword.requestFocus ();
636+ setState (() => isLoading = true );
637+ final email = controllerEmail.text.trim ();
638+ await firebaseAuth.sendPasswordResetEmail (email: email);
639+ setState (() => isLoading = false );
640+ _showSnackBar (
641+ context,
642+ 'We have sent a link reset password to your email' ,
643+ widthScreen,
644+ );
645+ Navigator .pop (context);
646+ }
647+ }
648+ }
649+
650+ String ? emailValidator (String ? value) {
651+ if (value == null || value.isEmpty) {
652+ return 'Enter an email address' ;
653+ } else {
654+ final isEmailValid = RegExp (
655+ r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+" ,
656+ ).hasMatch (value);
657+ return isEmailValid ? null : 'Invalid email' ;
658+ }
659+ }
0 commit comments