refactor following feedback

This commit is contained in:
Stein Milder 2022-09-27 10:43:22 +02:00
parent b40fc7fb95
commit 48f652e6ea
7 changed files with 282 additions and 298 deletions

View file

@ -29,7 +29,6 @@ abstract class LoginRespositoryWithPhoneNumber extends LoginRepository {
)? )?
onCodeSent, onCodeSent,
void Function(String errorCode)? onVerificationFailed, void Function(String errorCode)? onVerificationFailed,
void Function(LoginUser? user)? onAutoLogin,
}); });
Future<LoginUser?> signInWithSMSCode( Future<LoginUser?> signInWithSMSCode(
String verificationId, String verificationId,

View file

@ -30,13 +30,6 @@ enum LoginMethod {
LoginInteractiveWithPhoneNumber, LoginInteractiveWithPhoneNumber,
} }
enum AutoLoginMode {
alwaysOn,
alwaysOff,
defaultOn,
defaultOff,
}
enum SocialLoginMethod { enum SocialLoginMethod {
FaceBook, FaceBook,
Google, Google,
@ -151,7 +144,6 @@ class ConfigData {
class LoginOptions { class LoginOptions {
const LoginOptions({ const LoginOptions({
this.autoLoginMode = AutoLoginMode.defaultOn,
this.loginImage = '', this.loginImage = '',
this.loginEmail, this.loginEmail,
this.loginPassword, this.loginPassword,
@ -212,18 +204,6 @@ class LoginOptions {
/// If you want to change the registration background image, view [RegistrationOptions.backgroundImage] /// If you want to change the registration background image, view [RegistrationOptions.backgroundImage]
final String? backgroundImage; final String? backgroundImage;
/// [AutoLoginMode.defaultOn]
/// Auto login can be enabled/disabled using a checkbox on the login screen.
/// auto login is enabled by default.
/// [AutoLoginMode.defaultOff]
/// Auto login can be enabled/disabled using a checkbox on the login screen.
/// auto login is disabled by default.
/// [AutoLoginMode.alwaysOn]
/// Auto login is always on, there is no way to disable it.
/// [AutoLoginMode.alwaysOff]
/// Auto login is always off, there is no way to enable it.
final AutoLoginMode autoLoginMode;
final VoidCallback? onPressForgotPassword; final VoidCallback? onPressForgotPassword;
final VoidCallback? onPressRegister; final VoidCallback? onPressRegister;

View file

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login_view.dart'; import 'package:flutter_login/flutter_login_view.dart';
import '../form/inputs/validators/email_validator.dart';
class ForgotPassword extends StatefulWidget { class ForgotPassword extends StatefulWidget {
const ForgotPassword({super.key}); const ForgotPassword({super.key});
@ -10,8 +9,9 @@ class ForgotPassword extends StatefulWidget {
} }
class ForgotPasswordState extends State<ForgotPassword> { class ForgotPasswordState extends State<ForgotPassword> {
String? email; String _email = '';
bool showError = false; final _formKey = GlobalKey<FormState>();
String _emailErrorMessage = '';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -40,6 +40,40 @@ class ForgotPasswordState extends State<ForgotPassword> {
), ),
); );
void onPressBtn() {
if (!_formKey.currentState!.validate()) {
return;
}
context.loginRepository().forgotPassword(_email).then(
(response) {
if (response) {
showAlert(
title: context.translate(
'forgot_password.dialog.text.title',
),
text: context.translate(
'forgot_password.dialog.text.body',
arguments: [_email],
),
buttonTitle: context.translate(
'forgot_password.dialog.text.button',
),
buttonAction: () {
Navigator.pop(context);
},
);
} else {
_emailErrorMessage = context.translate(
'forgot_password.error.email_does_not_exist',
);
_formKey.currentState?.validate();
_emailErrorMessage = '';
}
},
);
}
return Material( return Material(
child: Scaffold( child: Scaffold(
backgroundColor: Theme.of(context).backgroundColor, backgroundColor: Theme.of(context).backgroundColor,
@ -61,50 +95,52 @@ class ForgotPasswordState extends State<ForgotPassword> {
textAlign: TextAlign.left, textAlign: TextAlign.left,
), ),
), ),
Padding( Form(
padding: const EdgeInsets.only(left: 30, right: 30), key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
left: 30,
right: 30,
),
child: Column( child: Column(
children: [ children: [
const Padding( const Padding(
padding: EdgeInsets.only(bottom: 20), padding: EdgeInsets.only(bottom: 20),
), ),
context.login().config.appTheme.inputs.textField( TextFormField(
title: context.translate( decoration: InputDecoration(
labelText: context.translate(
'forgot_password.input.email', 'forgot_password.input.email',
defaultValue: 'Email address',
), ),
validators: [
EmailValidator(
errorMessage: context.translate(
'forgot_password.error.invalid_email',
), ),
) onChanged: (String value) => setState(
], () {
onChange: (value, valid) { _email = value;
setState(() {
showError = false;
});
if (valid) {
email = value;
}
}, },
), ),
if (showError) ...[ validator: (value) {
Text( if (_emailErrorMessage.isNotEmpty) {
context.translate( return _emailErrorMessage;
'forgot_password.error.email_does_not_exist', }
if (!RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+",
).hasMatch(value!)) {
return context.translate(
'forgot_password.error.invalid_email',
);
}
return null;
},
), ),
style:
Theme.of(context).textTheme.bodyText2!.copyWith(
color: Theme.of(context).errorColor,
),
),
],
const Padding( const Padding(
padding: EdgeInsets.only(bottom: 30), padding: EdgeInsets.only(bottom: 30),
), ),
], ],
), ),
), ),
),
], ],
), ),
), ),
@ -117,39 +153,7 @@ class ForgotPasswordState extends State<ForgotPassword> {
context.translate('forgot_password.button.submit'), context.translate('forgot_password.button.submit'),
style: Theme.of(context).textTheme.button, style: Theme.of(context).textTheme.button,
), ),
onPressed: () async { onPressed: onPressBtn,
if (email != null) {
setState(() {
showError = false;
});
var result = await context
.loginRepository()
.forgotPassword(email!);
if (result) {
showAlert(
title: context.translate(
'forgot_password.dialog.text.title',
),
text: context.translate(
'forgot_password.dialog.text.body',
arguments: [email],
),
buttonTitle: context.translate(
'forgot_password.dialog.text.button',
),
buttonAction: () {
Navigator.pop(context);
},
);
} else {
setState(() {
showError = true;
});
}
}
},
), ),
), ),
], ],

View file

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login_view.dart'; import 'package:flutter_login/flutter_login_view.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../form/fields/obscure_text_input_field.dart';
import 'forgot_password.dart'; import 'forgot_password.dart';
import 'login_await_email.dart'; import 'login_await_email.dart';
import 'login_image.dart'; import 'login_image.dart';
@ -30,12 +29,13 @@ class EmailPasswordLogin extends Login {
class EmailLoginState extends LoginState<EmailPasswordLogin> { class EmailLoginState extends LoginState<EmailPasswordLogin> {
String email = ''; String email = '';
String error = '';
String password = ''; String password = '';
bool? autoLogin;
late SharedPreferences prefs; late SharedPreferences prefs;
bool passwordLess = false; bool passwordLess = false;
bool _loading = false; bool _loading = false;
final _formKey = GlobalKey<FormState>();
String _emailErrorMessage = '';
String _passwordErrorMessage = '';
@override @override
void initState() { void initState() {
@ -65,39 +65,46 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
} }
Future<void> _handleLoginPress() async { Future<void> _handleLoginPress() async {
if (!_formKey.currentState!.validate()) {
return;
}
var loginRepository = context.loginRepository();
setState(() { setState(() {
_loading = true; _loading = true;
}); });
await prefs.setBool(
'autoLogin',
context.login().config.loginOptions.autoLoginMode ==
AutoLoginMode.defaultOn,
);
if (!passwordLess && if (!passwordLess &&
(EmailPasswordLogin.finalEmail != null) && (EmailPasswordLogin.finalEmail != null) &&
(EmailPasswordLogin.finalPassword != null)) { (EmailPasswordLogin.finalPassword != null)) {
if (!(await context.loginRepository().login( await loginRepository
.login(
EmailPasswordLogin.finalEmail!, EmailPasswordLogin.finalEmail!,
EmailPasswordLogin.finalPassword!, EmailPasswordLogin.finalPassword!,
))) { )
setState(() { .then(
error = context.loginRepository().getLoginError(); (response) => response
_loading = false; ? navigateFadeToReplace(
});
} else {
navigateFadeToReplace(
context, context,
(context) => widget.child, (context) => widget.child,
popRemaining: true, popRemaining: true,
)
: setState(
() {
parseLoginMessage();
_loading = false;
},
),
); );
}
} else if (passwordLess && (EmailPasswordLogin.finalEmail != null)) { } else if (passwordLess && (EmailPasswordLogin.finalEmail != null)) {
if (await context await loginRepository
.loginRepository() .sendLoginEmail(
.sendLoginEmail(EmailPasswordLogin.finalEmail!)) { EmailPasswordLogin.finalEmail!,
navigateFadeToReplace( )
.then(
(response) => response
? navigateFadeToReplace(
context, context,
(ctx) => LoginAwaitEmailScreen( (ctx) => LoginAwaitEmailScreen(
child: widget.child, child: widget.child,
@ -106,15 +113,37 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
}, },
), ),
popRemaining: true, popRemaining: true,
); )
} else { : setState(
setState(() { () {
error = 'login.error.user_or_password_unknown'; _emailErrorMessage =
'login.error.user_or_password_unknown';
_loading = false; _loading = false;
},
),
);
}
_formKey.currentState!.validate();
_emailErrorMessage = '';
_passwordErrorMessage = '';
}
void parseLoginMessage() {
var loginRepository = context.loginRepository();
var loginError = loginRepository.getLoginError();
setState(() {
print(loginError);
switch (loginError) {
case 'login.error.invalid_email':
_emailErrorMessage = loginError;
break;
default:
_passwordErrorMessage = loginError;
}
}); });
} }
}
}
@override @override
Widget buildLoginPage(BuildContext context) { Widget buildLoginPage(BuildContext context) {
@ -167,34 +196,51 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
: 0, : 0,
), ),
child: Container( child: Container(
child: Form(
key: _formKey,
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
context.login().config.appTheme.inputs.textField( TextFormField(
value: EmailPasswordLogin.finalEmail ?? '', initialValue: EmailPasswordLogin.finalEmail ?? '',
onChange: (val, valid) { onChanged: onEmailChanged,
if (valid) {
onEmailChanged(val);
}
},
keyboardType: TextInputType.emailAddress, keyboardType: TextInputType.emailAddress,
title: context.translate( decoration: InputDecoration(
labelText: context.translate(
'login.input.email', 'login.input.email',
defaultValue: 'Email address', defaultValue: 'Email address',
), ),
), ),
validator: (value) {
if (_emailErrorMessage != '') {
return context.translate(_emailErrorMessage);
}
if (value == null || value.isEmpty) {
return 'Please enter an email address';
}
return null;
},
),
if (!passwordLess) ...[ if (!passwordLess) ...[
Padding( Padding(
padding: const EdgeInsets.only(top: 20), padding: const EdgeInsets.only(top: 20),
child: ObscureTextInputField( child: TextFormField(
value: EmailPasswordLogin.finalPassword, initialValue: EmailPasswordLogin.finalPassword ?? '',
title: context.translate( obscureText: true,
onChanged: onPasswordChanged,
decoration: InputDecoration(
labelText: context.translate(
'login.input.password', 'login.input.password',
defaultValue: 'Password', defaultValue: 'Password',
), ),
onChange: (val, valid) { ),
if (valid) { validator: (value) {
onPasswordChanged(val); if (_passwordErrorMessage != '') {
return context.translate(_passwordErrorMessage);
} }
if (value == null || value.isEmpty) {
return 'Please enter a password';
}
return null;
}, },
), ),
), ),
@ -225,58 +271,6 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
), ),
), ),
), ),
if (context.login().config.loginOptions.autoLoginMode !=
AutoLoginMode.alwaysOff &&
context.login().config.loginOptions.autoLoginMode !=
AutoLoginMode.alwaysOn) ...[
Theme(
data: Theme.of(context).copyWith(
textTheme: Theme.of(context).textTheme.copyWith(
headline6: Theme.of(context)
.textTheme
.bodyText2
?.copyWith(
color: Theme.of(context)
.textTheme
.headline6
?.color,
),
),
),
child: context.login().config.appTheme.inputs.checkBox(
value: autoLogin ??
context
.login()
.config
.loginOptions
.autoLoginMode ==
AutoLoginMode.defaultOn,
title: context.translate(
'login.input.stay_logged_in',
defaultValue: 'Stay logged in',
),
onChange: (value, valid) => autoLogin = value,
),
),
],
if (error.isNotEmpty) ...[
Container(
padding: const EdgeInsets.only(top: 20, bottom: 20),
child: Text(
error == ''
? error
: context.translate(
error,
defaultValue:
'An error occurred when logging in: $error',
),
style: Theme.of(context)
.textTheme
.bodyText1!
.copyWith(color: Theme.of(context).errorColor),
),
),
],
FlutterLogin.of(context) FlutterLogin.of(context)
.config .config
.appTheme .appTheme
@ -295,11 +289,22 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
), ),
onPressed: _handleLoginPress, onPressed: _handleLoginPress,
), ),
if (context.login().config.loginOptions.loginMethod.contains( if (context
.login()
.config
.loginOptions
.loginMethod
.contains(
LoginMethod.LoginInteractiveWithMagicLink, LoginMethod.LoginInteractiveWithMagicLink,
) && ) &&
context.login().config.loginOptions.loginMethod.contains( context
LoginMethod.LoginInteractiveWithUsernameAndPassword, .login()
.config
.loginOptions
.loginMethod
.contains(
LoginMethod
.LoginInteractiveWithUsernameAndPassword,
)) ...[ )) ...[
FlutterLogin.of(context) FlutterLogin.of(context)
.config .config
@ -351,6 +356,7 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
), ),
), ),
), ),
),
], ],
); );
} }

View file

@ -137,7 +137,6 @@ class LoginPhoneNumberState extends State<LoginPhoneNumber>
onLogin: _onLoggedIn, onLogin: _onLoggedIn,
), ),
), ),
onAutoLogin: (_) => _onLoggedIn,
onVerificationFailed: (String errorCode) { onVerificationFailed: (String errorCode) {
if (errorCode == 'invalid-phone-number') { if (errorCode == 'invalid-phone-number') {
setState(() { setState(() {
@ -170,8 +169,6 @@ class LoginPhoneNumberState extends State<LoginPhoneNumber>
} }
Future<void> _onLoggedIn(LoginUser? user) async { Future<void> _onLoggedIn(LoginUser? user) async {
var prefs = await SharedPreferences.getInstance();
await prefs.setBool('autoLogin', true);
if (user != null) { if (user != null) {
if (await context.loginRepository().isRegistrationRequired(user)) { if (await context.loginRepository().isRegistrationRequired(user)) {
widget.navRegistration.call(); widget.navRegistration.call();

View file

@ -162,7 +162,6 @@ class LoginPhoneNumberVerifyState extends State<LoginPhoneNumberVerify>
), ),
); );
}, },
onAutoLogin: (user) => widget.onLogin.call(user),
); );
}, },
), ),

View file

@ -83,8 +83,7 @@ class LoginSocialButtons extends StatelessWidget {
interactionType: SocialInteractionType.Login, interactionType: SocialInteractionType.Login,
), ),
); );
var prefs = await SharedPreferences.getInstance();
await prefs.setBool('autoLogin', true);
if (user != null) { if (user != null) {
if (await repository.isRegistrationRequired(user)) { if (await repository.isRegistrationRequired(user)) {
navigateRegistration(); navigateRegistration();