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,
void Function(String errorCode)? onVerificationFailed,
void Function(LoginUser? user)? onAutoLogin,
});
Future<LoginUser?> signInWithSMSCode(
String verificationId,

View file

@ -30,13 +30,6 @@ enum LoginMethod {
LoginInteractiveWithPhoneNumber,
}
enum AutoLoginMode {
alwaysOn,
alwaysOff,
defaultOn,
defaultOff,
}
enum SocialLoginMethod {
FaceBook,
Google,
@ -151,7 +144,6 @@ class ConfigData {
class LoginOptions {
const LoginOptions({
this.autoLoginMode = AutoLoginMode.defaultOn,
this.loginImage = '',
this.loginEmail,
this.loginPassword,
@ -212,18 +204,6 @@ class LoginOptions {
/// If you want to change the registration background image, view [RegistrationOptions.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? onPressRegister;

View file

@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login_view.dart';
import '../form/inputs/validators/email_validator.dart';
class ForgotPassword extends StatefulWidget {
const ForgotPassword({super.key});
@ -10,8 +9,9 @@ class ForgotPassword extends StatefulWidget {
}
class ForgotPasswordState extends State<ForgotPassword> {
String? email;
bool showError = false;
String _email = '';
final _formKey = GlobalKey<FormState>();
String _emailErrorMessage = '';
@override
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(
child: Scaffold(
backgroundColor: Theme.of(context).backgroundColor,
@ -61,48 +95,50 @@ class ForgotPasswordState extends State<ForgotPassword> {
textAlign: TextAlign.left,
),
),
Padding(
padding: const EdgeInsets.only(left: 30, right: 30),
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(bottom: 20),
),
context.login().config.appTheme.inputs.textField(
title: context.translate(
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.only(
left: 30,
right: 30,
),
child: Column(
children: [
const Padding(
padding: EdgeInsets.only(bottom: 20),
),
TextFormField(
decoration: InputDecoration(
labelText: context.translate(
'forgot_password.input.email',
defaultValue: 'Email address',
),
validators: [
EmailValidator(
errorMessage: context.translate(
'forgot_password.error.invalid_email',
),
)
],
onChange: (value, valid) {
setState(() {
showError = false;
});
if (valid) {
email = value;
}
),
onChanged: (String value) => setState(
() {
_email = value;
},
),
if (showError) ...[
Text(
context.translate(
'forgot_password.error.email_does_not_exist',
),
style:
Theme.of(context).textTheme.bodyText2!.copyWith(
color: Theme.of(context).errorColor,
),
validator: (value) {
if (_emailErrorMessage.isNotEmpty) {
return _emailErrorMessage;
}
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;
},
),
const Padding(
padding: EdgeInsets.only(bottom: 30),
),
],
const Padding(
padding: EdgeInsets.only(bottom: 30),
),
],
),
),
),
],
@ -117,39 +153,7 @@ class ForgotPasswordState extends State<ForgotPassword> {
context.translate('forgot_password.button.submit'),
style: Theme.of(context).textTheme.button,
),
onPressed: () async {
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;
});
}
}
},
onPressed: onPressBtn,
),
),
],

View file

@ -2,7 +2,6 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_login/flutter_login_view.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../form/fields/obscure_text_input_field.dart';
import 'forgot_password.dart';
import 'login_await_email.dart';
import 'login_image.dart';
@ -30,12 +29,13 @@ class EmailPasswordLogin extends Login {
class EmailLoginState extends LoginState<EmailPasswordLogin> {
String email = '';
String error = '';
String password = '';
bool? autoLogin;
late SharedPreferences prefs;
bool passwordLess = false;
bool _loading = false;
final _formKey = GlobalKey<FormState>();
String _emailErrorMessage = '';
String _passwordErrorMessage = '';
@override
void initState() {
@ -65,55 +65,84 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
}
Future<void> _handleLoginPress() async {
if (!_formKey.currentState!.validate()) {
return;
}
var loginRepository = context.loginRepository();
setState(() {
_loading = true;
});
await prefs.setBool(
'autoLogin',
context.login().config.loginOptions.autoLoginMode ==
AutoLoginMode.defaultOn,
);
if (!passwordLess &&
(EmailPasswordLogin.finalEmail != null) &&
(EmailPasswordLogin.finalPassword != null)) {
if (!(await context.loginRepository().login(
await loginRepository
.login(
EmailPasswordLogin.finalEmail!,
EmailPasswordLogin.finalPassword!,
))) {
setState(() {
error = context.loginRepository().getLoginError();
_loading = false;
});
} else {
navigateFadeToReplace(
context,
(context) => widget.child,
popRemaining: true,
);
}
)
.then(
(response) => response
? navigateFadeToReplace(
context,
(context) => widget.child,
popRemaining: true,
)
: setState(
() {
parseLoginMessage();
_loading = false;
},
),
);
} else if (passwordLess && (EmailPasswordLogin.finalEmail != null)) {
if (await context
.loginRepository()
.sendLoginEmail(EmailPasswordLogin.finalEmail!)) {
navigateFadeToReplace(
context,
(ctx) => LoginAwaitEmailScreen(
child: widget.child,
loginComplete: () async {
navigateFadeToReplace(ctx, (ctx) => widget.child!);
},
),
popRemaining: true,
);
} else {
setState(() {
error = 'login.error.user_or_password_unknown';
_loading = false;
});
}
await loginRepository
.sendLoginEmail(
EmailPasswordLogin.finalEmail!,
)
.then(
(response) => response
? navigateFadeToReplace(
context,
(ctx) => LoginAwaitEmailScreen(
child: widget.child,
loginComplete: () async {
navigateFadeToReplace(ctx, (ctx) => widget.child!);
},
),
popRemaining: true,
)
: setState(
() {
_emailErrorMessage =
'login.error.user_or_password_unknown';
_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
@ -167,186 +196,163 @@ class EmailLoginState extends LoginState<EmailPasswordLogin> {
: 0,
),
child: Container(
child: Column(
children: <Widget>[
context.login().config.appTheme.inputs.textField(
value: EmailPasswordLogin.finalEmail ?? '',
onChange: (val, valid) {
if (valid) {
onEmailChanged(val);
}
},
keyboardType: TextInputType.emailAddress,
title: context.translate(
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
initialValue: EmailPasswordLogin.finalEmail ?? '',
onChanged: onEmailChanged,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: context.translate(
'login.input.email',
defaultValue: 'Email address',
),
),
if (!passwordLess) ...[
Padding(
padding: const EdgeInsets.only(top: 20),
child: ObscureTextInputField(
value: EmailPasswordLogin.finalPassword,
title: context.translate(
'login.input.password',
defaultValue: 'Password',
),
onChange: (val, valid) {
if (valid) {
onPasswordChanged(val);
}
},
),
validator: (value) {
if (_emailErrorMessage != '') {
return context.translate(_emailErrorMessage);
}
if (value == null || value.isEmpty) {
return 'Please enter an email address';
}
return null;
},
),
],
const Padding(
padding: EdgeInsets.only(top: 20),
),
Container(
alignment: Alignment.centerRight,
child: context
.login()
.config
.appTheme
.buttons
.tertiaryButton(
context: context,
child: Text(
context.translate('login.button.forgot_password'),
style: Theme.of(context).textTheme.bodyText2,
if (!passwordLess) ...[
Padding(
padding: const EdgeInsets.only(top: 20),
child: TextFormField(
initialValue: EmailPasswordLogin.finalPassword ?? '',
obscureText: true,
onChanged: onPasswordChanged,
decoration: InputDecoration(
labelText: context.translate(
'login.input.password',
defaultValue: 'Password',
),
),
onPressed: widget.onPressedForgotPassword ??
() => Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const ForgotPassword(),
),
),
validator: (value) {
if (_passwordErrorMessage != '') {
return context.translate(_passwordErrorMessage);
}
if (value == null || value.isEmpty) {
return 'Please enter a password';
}
return null;
},
),
),
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,
),
],
const Padding(
padding: EdgeInsets.only(top: 20),
),
],
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)
.config
.appTheme
.buttons
.primaryButton(
context: context,
isLoading: _loading,
isDisabled: _loading,
child: Text(
context.translate(
'login.button.login',
defaultValue: 'Log in',
alignment: Alignment.centerRight,
child: context
.login()
.config
.appTheme
.buttons
.tertiaryButton(
context: context,
child: Text(
context.translate('login.button.forgot_password'),
style: Theme.of(context).textTheme.bodyText2,
),
onPressed: widget.onPressedForgotPassword ??
() => Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const ForgotPassword(),
),
),
),
style: Theme.of(context).textTheme.button,
textAlign: TextAlign.center,
),
onPressed: _handleLoginPress,
),
if (context.login().config.loginOptions.loginMethod.contains(
LoginMethod.LoginInteractiveWithMagicLink,
) &&
context.login().config.loginOptions.loginMethod.contains(
LoginMethod.LoginInteractiveWithUsernameAndPassword,
)) ...[
),
FlutterLogin.of(context)
.config
.appTheme
.buttons
.tertiaryButton(
context: context,
child: Text(
passwordLess
? context.translate(
'login.button.login_email_password',
defaultValue: 'Log in with password',
)
: context.translate(
'login.button.login_email_only',
defaultValue: 'Log in with link',
),
style: Theme.of(context).textTheme.bodyText1,
),
onPressed: () =>
setState(() => passwordLess = !passwordLess),
)
],
if (context
.login()
.config
.registrationOptions
.registrationMode ==
RegistrationMode.Enabled) ...[
context.login().config.appTheme.buttons.tertiaryButton(
.primaryButton(
context: context,
isLoading: _loading,
isDisabled: _loading,
child: Text(
context.translate(
'login.button.no_account',
defaultValue: "I don't have an account yet",
'login.button.login',
defaultValue: 'Log in',
),
style: Theme.of(context).textTheme.bodyText1,
style: Theme.of(context).textTheme.button,
textAlign: TextAlign.center,
),
onPressed: () {
FlutterLogin.of(context)
.config
.loginOptions
.onPressRegister!();
},
onPressed: _handleLoginPress,
),
if (context
.login()
.config
.loginOptions
.loginMethod
.contains(
LoginMethod.LoginInteractiveWithMagicLink,
) &&
context
.login()
.config
.loginOptions
.loginMethod
.contains(
LoginMethod
.LoginInteractiveWithUsernameAndPassword,
)) ...[
FlutterLogin.of(context)
.config
.appTheme
.buttons
.tertiaryButton(
context: context,
child: Text(
passwordLess
? context.translate(
'login.button.login_email_password',
defaultValue: 'Log in with password',
)
: context.translate(
'login.button.login_email_only',
defaultValue: 'Log in with link',
),
style: Theme.of(context).textTheme.bodyText1,
),
onPressed: () =>
setState(() => passwordLess = !passwordLess),
)
],
if (context
.login()
.config
.registrationOptions
.registrationMode ==
RegistrationMode.Enabled) ...[
context.login().config.appTheme.buttons.tertiaryButton(
context: context,
child: Text(
context.translate(
'login.button.no_account',
defaultValue: "I don't have an account yet",
),
style: Theme.of(context).textTheme.bodyText1,
),
onPressed: () {
FlutterLogin.of(context)
.config
.loginOptions
.onPressRegister!();
},
),
],
],
],
),
),
),
),

View file

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