Skip to content
Merged
192 changes: 51 additions & 141 deletions sama_chat_client/lib/src/features/login/view/login_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:go_router/go_router.dart';

import '../../../navigation/constants.dart';
import '../../../shared/ui/colors.dart';
import '../../../shared/ui/view/text_field_form.dart';
import '../bloc/login_bloc.dart';
import '../models/models.dart';

Expand Down Expand Up @@ -105,20 +106,6 @@ class LoginFormState extends State<LoginForm> {
const Padding(padding: EdgeInsets.all(8)),
_EmailInput(),
const Padding(padding: EdgeInsets.all(4)),
Row(
children: [
Checkbox(
checkColor: white,
activeColor: whiteAluminum,
value: loginWithNewUser,
onChanged: (checked) {
setState(() {
loginWithNewUser = checked ?? true;
});
}),
const Text('* Sign in automatically')
],
),
] else ...[
const Padding(padding: EdgeInsets.all(4)),
Align(
Expand Down Expand Up @@ -170,40 +157,16 @@ class _UsernameInput extends StatelessWidget {
return BlocBuilder<LoginBloc, LoginState>(
buildWhen: (previous, current) => previous.username != current.username,
builder: (context, state) {
return Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(12)),
color: gainsborough,
),
child: TextField(
key: const Key('loginForm_usernameInput_textField'),
keyboardType: TextInputType.text,
return TextFieldForm(
onChanged: (username) =>
context.read<LoginBloc>().add(LoginUsernameChanged(username)),
decoration: InputDecoration(
border: InputBorder.none,
label: const Row(
children: [
Icon(
Icons.person_outlined,
size: 16,
color: dullGray,
),
Text(
'Username',
style: TextStyle(color: dullGray, fontSize: 16),
)
],
),
errorText: state.username.displayError != null
? state.username.displayError == UsernameValidationError.short
? 'User name is too short'
: null
: null,
),
),
);
iconData: Icons.person_outlined,
hint: 'Username',
error: state.username.displayError != null
? state.username.displayError == UsernameValidationError.short
? 'User name is too short'
: null
: null);
},
);
}
Expand All @@ -224,68 +187,41 @@ class _PasswordInputState extends State<_PasswordInput> {
return BlocBuilder<LoginBloc, LoginState>(
buildWhen: (previous, current) => previous.password != current.password,
builder: (context, state) {
return Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(12),
),
color: gainsborough,
),
child: TextField(
key: const Key('loginForm_passwordInput_textField'),
return TextFieldForm(
keyboardType: TextInputType.visiblePassword,
onChanged: (password) =>
context.read<LoginBloc>().add(LoginPasswordChanged(password)),
iconData: Icons.lock_outline,
obscureText: isPasswordInvisible,
obscuringCharacter: '*',
enableSuggestions: false,
autocorrect: false,
decoration: InputDecoration(
border: InputBorder.none,
label: const Row(
children: [
Icon(
Icons.lock_outline,
size: 16,
color: dullGray,
),
Text(
'Password',
style: TextStyle(color: dullGray, fontSize: 16),
)
],
),
errorText: state.password.displayError != null
? state.password.displayError == PasswordValidationError.short
? 'Password is too short'
: state.password.displayError ==
PasswordValidationError.long
? 'Password is too long'
: state.password.displayError ==
PasswordValidationError.unavailableSymbols
? 'Password contains not allowed symbols'
: null
: null,
suffixIcon: Padding(
padding: const EdgeInsets.only(right: 8),
child: IconButton(
onPressed: () {
setState(() {
isPasswordInvisible = !isPasswordInvisible;
});
},
icon: Icon(
isPasswordInvisible
? Icons.visibility_off_outlined
: Icons.visibility_outlined,
color: dullGray,
),
suffix: Padding(
padding: const EdgeInsets.only(right: 8),
child: IconButton(
onPressed: () {
setState(() {
isPasswordInvisible = !isPasswordInvisible;
});
},
icon: Icon(
isPasswordInvisible
? Icons.visibility_off_outlined
: Icons.visibility_outlined,
color: dullGray,
size: 20,
),
),
),
),
);
hint: 'Password',
error: state.password.displayError != null
? state.password.displayError == PasswordValidationError.short
? 'Password is too short'
: state.password.displayError ==
PasswordValidationError.long
? 'Password is too long'
: state.password.displayError ==
PasswordValidationError.unavailableSymbols
? 'Password contains not allowed symbols'
: null
: null);
},
);
}
Expand All @@ -297,46 +233,19 @@ class _EmailInput extends StatelessWidget {
return BlocBuilder<LoginBloc, LoginState>(
buildWhen: (previous, current) => previous.email != current.email,
builder: (context, state) {
return Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(
Radius.circular(12),
),
color: gainsborough,
),
child: TextField(
keyboardType: TextInputType.emailAddress,
return TextFieldForm(
onChanged: (email) =>
context.read<LoginBloc>().add(LoginEmailChanged(email)),
enableSuggestions: false,
autocorrect: false,
decoration: InputDecoration(
border: InputBorder.none,
label: const Row(
children: [
Icon(
Icons.email_outlined,
size: 16,
color: dullGray,
),
Padding(padding: EdgeInsets.all(4)),
Text(
'Email',
style: TextStyle(color: dullGray, fontSize: 16),
)
],
),
errorText: state.email.displayError != null
? state.email.displayError == EmailValidationError.empty
? 'Email is too short'
: state.email.displayError ==
EmailValidationError.incorrect
? 'The format of the email address is incorrect'
: null
: null),
),
);
iconData: Icons.email_outlined,
keyboardType: TextInputType.emailAddress,
hint: 'Email',
error: state.email.displayError != null
? state.email.displayError == EmailValidationError.empty
? 'Email is too short'
: state.email.displayError == EmailValidationError.incorrect
? 'The format of the email address is incorrect'
: null
: null);
},
);
}
Expand Down Expand Up @@ -374,7 +283,7 @@ class _LoginButton extends StatelessWidget {
isSignInValid || isSignUpValid ? white : gainsborough),
shape: WidgetStatePropertyAll<RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12.0),
borderRadius: BorderRadius.circular(22),
),
),
),
Expand All @@ -386,7 +295,8 @@ class _LoginButton extends StatelessWidget {
);
}
: null,
child: Text(isSignup ? 'Create account' : 'Login'),
child: Text(isSignup ? 'Create account' : 'Login',
style: const TextStyle(fontSize: 16)),
),
);
},
Expand Down
64 changes: 64 additions & 0 deletions sama_chat_client/lib/src/shared/ui/view/text_field_form.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import 'package:flutter/material.dart';

import '../colors.dart';

class TextFieldForm extends StatelessWidget {
final ValueChanged<String>? onChanged;
final IconData iconData;
final String hint;
final TextInputType? keyboardType;
final String? error;
final Widget? suffix;
final bool? obscureText;

const TextFieldForm(
{super.key,
required this.onChanged,
required this.iconData,
required this.hint,
this.keyboardType,
this.error,
this.suffix,
this.obscureText});

@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.all(4),
height: 55,
decoration: const BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(28)),
color: gainsborough,
),
child: TextField(
keyboardType: keyboardType,
style: const TextStyle(fontSize: 17),
autocorrect: false,
enableSuggestions: false,
obscuringCharacter: '*',
obscureText: obscureText ?? false,
onChanged: (username) => onChanged,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: const EdgeInsets.only(left: 12.0),
isDense: true,
label: Row(
spacing: 10,
children: [
Icon(
iconData,
size: 26,
color: dullGray,
),
Text(
hint,
style: const TextStyle(color: dullGray, fontSize: 16),
)
],
),
errorText: error,
suffixIcon: suffix ?? const SizedBox(),
),
));
}
}