mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
refactor sign in screen according to the ui
This commit is contained in:
parent
084f939ed0
commit
771162e80b
@ -15,7 +15,7 @@ Future<void> initGetIt(
|
||||
getIt.registerLazySingleton<FlowySDK>(() => const FlowySDK());
|
||||
getIt.registerLazySingleton<AppLauncher>(() => AppLauncher(env, getIt));
|
||||
|
||||
await WelcomeDepsResolver.resolve(getIt);
|
||||
await UserDepsResolver.resolve(getIt);
|
||||
await WelcomeDepsResolver.resolve(getIt);
|
||||
await HomeDepsResolver.resolve(getIt);
|
||||
}
|
||||
|
@ -22,10 +22,10 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
|
||||
);
|
||||
},
|
||||
emailChanged: (EmailChanged value) async* {
|
||||
yield state.copyWith(email: value.email, signInFailure: none());
|
||||
yield state.copyWith(email: value.email, successOrFail: none());
|
||||
},
|
||||
passwordChanged: (PasswordChanged value) async* {
|
||||
yield state.copyWith(password: value.password, signInFailure: none());
|
||||
yield state.copyWith(password: value.password, successOrFail: none());
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -36,17 +36,34 @@ class SignInBloc extends Bloc<SignInEvent, SignInState> {
|
||||
final result = await authImpl.signIn(state.email, state.password);
|
||||
yield result.fold(
|
||||
(userDetail) => state.copyWith(
|
||||
isSubmitting: false, signInFailure: some(left(userDetail))),
|
||||
(s) => state.copyWith(isSubmitting: false, signInFailure: some(right(s))),
|
||||
isSubmitting: false, successOrFail: some(left(userDetail))),
|
||||
(error) => stateFromCode(error),
|
||||
);
|
||||
}
|
||||
|
||||
SignInState stateFromCode(UserError error) {
|
||||
switch (error.code) {
|
||||
case UserErrCode.EmailInvalid:
|
||||
return state.copyWith(
|
||||
isSubmitting: false,
|
||||
emailError: some(error.msg),
|
||||
passwordError: none());
|
||||
case UserErrCode.PasswordInvalid:
|
||||
return state.copyWith(
|
||||
isSubmitting: false,
|
||||
passwordError: some(error.msg),
|
||||
emailError: none());
|
||||
default:
|
||||
return state.copyWith(
|
||||
isSubmitting: false, successOrFail: some(right(error)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class SignInEvent with _$SignInEvent {
|
||||
const factory SignInEvent.signedInWithUserEmailAndPassword() =
|
||||
SignedInWithUserEmailAndPassword;
|
||||
|
||||
const factory SignInEvent.emailChanged(String email) = EmailChanged;
|
||||
const factory SignInEvent.passwordChanged(String password) = PasswordChanged;
|
||||
}
|
||||
@ -57,11 +74,15 @@ abstract class SignInState with _$SignInState {
|
||||
String? email,
|
||||
String? password,
|
||||
required bool isSubmitting,
|
||||
required Option<Either<UserDetail, UserError>> signInFailure,
|
||||
required Option<String> passwordError,
|
||||
required Option<String> emailError,
|
||||
required Option<Either<UserDetail, UserError>> successOrFail,
|
||||
}) = _SignInState;
|
||||
|
||||
factory SignInState.initial() => SignInState(
|
||||
isSubmitting: false,
|
||||
signInFailure: none(),
|
||||
passwordError: none(),
|
||||
emailError: none(),
|
||||
successOrFail: none(),
|
||||
);
|
||||
}
|
||||
|
@ -438,12 +438,16 @@ class _$SignInStateTearOff {
|
||||
{String? email,
|
||||
String? password,
|
||||
required bool isSubmitting,
|
||||
required Option<Either<UserDetail, UserError>> signInFailure}) {
|
||||
required Option<String> passwordError,
|
||||
required Option<String> emailError,
|
||||
required Option<Either<UserDetail, UserError>> successOrFail}) {
|
||||
return _SignInState(
|
||||
email: email,
|
||||
password: password,
|
||||
isSubmitting: isSubmitting,
|
||||
signInFailure: signInFailure,
|
||||
passwordError: passwordError,
|
||||
emailError: emailError,
|
||||
successOrFail: successOrFail,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -456,7 +460,9 @@ mixin _$SignInState {
|
||||
String? get email => throw _privateConstructorUsedError;
|
||||
String? get password => throw _privateConstructorUsedError;
|
||||
bool get isSubmitting => throw _privateConstructorUsedError;
|
||||
Option<Either<UserDetail, UserError>> get signInFailure =>
|
||||
Option<String> get passwordError => throw _privateConstructorUsedError;
|
||||
Option<String> get emailError => throw _privateConstructorUsedError;
|
||||
Option<Either<UserDetail, UserError>> get successOrFail =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@ -473,7 +479,9 @@ abstract class $SignInStateCopyWith<$Res> {
|
||||
{String? email,
|
||||
String? password,
|
||||
bool isSubmitting,
|
||||
Option<Either<UserDetail, UserError>> signInFailure});
|
||||
Option<String> passwordError,
|
||||
Option<String> emailError,
|
||||
Option<Either<UserDetail, UserError>> successOrFail});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -489,7 +497,9 @@ class _$SignInStateCopyWithImpl<$Res> implements $SignInStateCopyWith<$Res> {
|
||||
Object? email = freezed,
|
||||
Object? password = freezed,
|
||||
Object? isSubmitting = freezed,
|
||||
Object? signInFailure = freezed,
|
||||
Object? passwordError = freezed,
|
||||
Object? emailError = freezed,
|
||||
Object? successOrFail = freezed,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
email: email == freezed
|
||||
@ -504,9 +514,17 @@ class _$SignInStateCopyWithImpl<$Res> implements $SignInStateCopyWith<$Res> {
|
||||
? _value.isSubmitting
|
||||
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
signInFailure: signInFailure == freezed
|
||||
? _value.signInFailure
|
||||
: signInFailure // ignore: cast_nullable_to_non_nullable
|
||||
passwordError: passwordError == freezed
|
||||
? _value.passwordError
|
||||
: passwordError // ignore: cast_nullable_to_non_nullable
|
||||
as Option<String>,
|
||||
emailError: emailError == freezed
|
||||
? _value.emailError
|
||||
: emailError // ignore: cast_nullable_to_non_nullable
|
||||
as Option<String>,
|
||||
successOrFail: successOrFail == freezed
|
||||
? _value.successOrFail
|
||||
: successOrFail // ignore: cast_nullable_to_non_nullable
|
||||
as Option<Either<UserDetail, UserError>>,
|
||||
));
|
||||
}
|
||||
@ -523,7 +541,9 @@ abstract class _$SignInStateCopyWith<$Res>
|
||||
{String? email,
|
||||
String? password,
|
||||
bool isSubmitting,
|
||||
Option<Either<UserDetail, UserError>> signInFailure});
|
||||
Option<String> passwordError,
|
||||
Option<String> emailError,
|
||||
Option<Either<UserDetail, UserError>> successOrFail});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -541,7 +561,9 @@ class __$SignInStateCopyWithImpl<$Res> extends _$SignInStateCopyWithImpl<$Res>
|
||||
Object? email = freezed,
|
||||
Object? password = freezed,
|
||||
Object? isSubmitting = freezed,
|
||||
Object? signInFailure = freezed,
|
||||
Object? passwordError = freezed,
|
||||
Object? emailError = freezed,
|
||||
Object? successOrFail = freezed,
|
||||
}) {
|
||||
return _then(_SignInState(
|
||||
email: email == freezed
|
||||
@ -556,9 +578,17 @@ class __$SignInStateCopyWithImpl<$Res> extends _$SignInStateCopyWithImpl<$Res>
|
||||
? _value.isSubmitting
|
||||
: isSubmitting // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
signInFailure: signInFailure == freezed
|
||||
? _value.signInFailure
|
||||
: signInFailure // ignore: cast_nullable_to_non_nullable
|
||||
passwordError: passwordError == freezed
|
||||
? _value.passwordError
|
||||
: passwordError // ignore: cast_nullable_to_non_nullable
|
||||
as Option<String>,
|
||||
emailError: emailError == freezed
|
||||
? _value.emailError
|
||||
: emailError // ignore: cast_nullable_to_non_nullable
|
||||
as Option<String>,
|
||||
successOrFail: successOrFail == freezed
|
||||
? _value.successOrFail
|
||||
: successOrFail // ignore: cast_nullable_to_non_nullable
|
||||
as Option<Either<UserDetail, UserError>>,
|
||||
));
|
||||
}
|
||||
@ -571,7 +601,9 @@ class _$_SignInState implements _SignInState {
|
||||
{this.email,
|
||||
this.password,
|
||||
required this.isSubmitting,
|
||||
required this.signInFailure});
|
||||
required this.passwordError,
|
||||
required this.emailError,
|
||||
required this.successOrFail});
|
||||
|
||||
@override
|
||||
final String? email;
|
||||
@ -580,11 +612,15 @@ class _$_SignInState implements _SignInState {
|
||||
@override
|
||||
final bool isSubmitting;
|
||||
@override
|
||||
final Option<Either<UserDetail, UserError>> signInFailure;
|
||||
final Option<String> passwordError;
|
||||
@override
|
||||
final Option<String> emailError;
|
||||
@override
|
||||
final Option<Either<UserDetail, UserError>> successOrFail;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SignInState(email: $email, password: $password, isSubmitting: $isSubmitting, signInFailure: $signInFailure)';
|
||||
return 'SignInState(email: $email, password: $password, isSubmitting: $isSubmitting, passwordError: $passwordError, emailError: $emailError, successOrFail: $successOrFail)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -599,9 +635,15 @@ class _$_SignInState implements _SignInState {
|
||||
(identical(other.isSubmitting, isSubmitting) ||
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.isSubmitting, isSubmitting)) &&
|
||||
(identical(other.signInFailure, signInFailure) ||
|
||||
(identical(other.passwordError, passwordError) ||
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.signInFailure, signInFailure)));
|
||||
.equals(other.passwordError, passwordError)) &&
|
||||
(identical(other.emailError, emailError) ||
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.emailError, emailError)) &&
|
||||
(identical(other.successOrFail, successOrFail) ||
|
||||
const DeepCollectionEquality()
|
||||
.equals(other.successOrFail, successOrFail)));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -610,7 +652,9 @@ class _$_SignInState implements _SignInState {
|
||||
const DeepCollectionEquality().hash(email) ^
|
||||
const DeepCollectionEquality().hash(password) ^
|
||||
const DeepCollectionEquality().hash(isSubmitting) ^
|
||||
const DeepCollectionEquality().hash(signInFailure);
|
||||
const DeepCollectionEquality().hash(passwordError) ^
|
||||
const DeepCollectionEquality().hash(emailError) ^
|
||||
const DeepCollectionEquality().hash(successOrFail);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -623,7 +667,9 @@ abstract class _SignInState implements SignInState {
|
||||
{String? email,
|
||||
String? password,
|
||||
required bool isSubmitting,
|
||||
required Option<Either<UserDetail, UserError>> signInFailure}) =
|
||||
required Option<String> passwordError,
|
||||
required Option<String> emailError,
|
||||
required Option<Either<UserDetail, UserError>> successOrFail}) =
|
||||
_$_SignInState;
|
||||
|
||||
@override
|
||||
@ -633,7 +679,11 @@ abstract class _SignInState implements SignInState {
|
||||
@override
|
||||
bool get isSubmitting => throw _privateConstructorUsedError;
|
||||
@override
|
||||
Option<Either<UserDetail, UserError>> get signInFailure =>
|
||||
Option<String> get passwordError => throw _privateConstructorUsedError;
|
||||
@override
|
||||
Option<String> get emailError => throw _privateConstructorUsedError;
|
||||
@override
|
||||
Option<Either<UserDetail, UserError>> get successOrFail =>
|
||||
throw _privateConstructorUsedError;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
abstract class IAuth {
|
||||
Future<Either<UserDetail, UserError>> signIn(String? email, String? password);
|
||||
@ -8,3 +9,9 @@ abstract class IAuth {
|
||||
|
||||
Future<Either<Unit, UserError>> signOut();
|
||||
}
|
||||
|
||||
abstract class IAuthRouter {
|
||||
void showHomeScreen(BuildContext context, UserDetail user);
|
||||
void showSignUpScreen(BuildContext context);
|
||||
void showForgetPasswordScreen(BuildContext context);
|
||||
}
|
||||
|
@ -6,10 +6,11 @@ import 'package:get_it/get_it.dart';
|
||||
|
||||
class UserDepsResolver {
|
||||
static Future<void> resolve(GetIt getIt) async {
|
||||
getIt.registerLazySingleton<AuthRepository>(() => AuthRepository());
|
||||
getIt.registerFactory<AuthRepository>(() => AuthRepository());
|
||||
|
||||
//Interface implementation
|
||||
getIt.registerFactory<IAuth>(() => AuthImpl(repo: getIt<AuthRepository>()));
|
||||
getIt.registerFactory<IAuthRouter>(() => AuthRouterImpl());
|
||||
|
||||
//Bloc
|
||||
getIt.registerFactory<SignInBloc>(() => SignInBloc(getIt<IAuth>()));
|
||||
|
@ -1,7 +1,9 @@
|
||||
import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:app_flowy/user/domain/i_auth.dart';
|
||||
import 'package:app_flowy/user/infrastructure/repos/auth_repo.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AuthImpl extends IAuth {
|
||||
AuthRepository repo;
|
||||
@ -26,3 +28,27 @@ class AuthImpl extends IAuth {
|
||||
return repo.signOut();
|
||||
}
|
||||
}
|
||||
|
||||
class AuthRouterImpl extends IAuthRouter {
|
||||
@override
|
||||
void showForgetPasswordScreen(BuildContext context) {
|
||||
// TODO: implement showForgetPasswordScreen
|
||||
}
|
||||
|
||||
@override
|
||||
void showHomeScreen(BuildContext context, UserDetail user) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return HomeScreen(user);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void showSignUpScreen(BuildContext context) {
|
||||
// TODO: implement showSignUpScreen
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/user/application/sign_in/sign_in_bloc.dart';
|
||||
import 'package:app_flowy/user/domain/i_auth.dart';
|
||||
import 'package:app_flowy/user/presentation/sign_in/widgets/background.dart';
|
||||
import 'package:app_flowy/workspace/presentation/home/home_screen.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
|
||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||
@ -12,35 +12,32 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
|
||||
class SignInScreen extends StatelessWidget {
|
||||
const SignInScreen({Key? key}) : super(key: key);
|
||||
final IAuthRouter router;
|
||||
const SignInScreen({Key? key, required this.router}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<SignInBloc>(),
|
||||
child: Scaffold(
|
||||
body: BlocProvider(
|
||||
create: (context) => getIt<SignInBloc>(),
|
||||
child: BlocConsumer<SignInBloc, SignInState>(
|
||||
listenWhen: (p, c) => p != c,
|
||||
listener: (context, state) {
|
||||
state.signInFailure.fold(
|
||||
() {},
|
||||
(result) => _handleStateErrors(result, context),
|
||||
);
|
||||
},
|
||||
builder: (context, state) => const SignInForm(),
|
||||
),
|
||||
child: BlocListener<SignInBloc, SignInState>(
|
||||
listener: (context, state) {
|
||||
state.successOrFail.fold(
|
||||
() => null,
|
||||
(result) => _handleSuccessOrFail(result, context),
|
||||
);
|
||||
},
|
||||
child: Scaffold(
|
||||
body: SignInForm(router: router),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleStateErrors(
|
||||
Either<UserDetail, UserError> some, BuildContext context) {
|
||||
some.fold(
|
||||
(userDetail) => _showHomeScreen(context, userDetail),
|
||||
(result) => _showErrorMessage(context, result.msg),
|
||||
void _handleSuccessOrFail(
|
||||
Either<UserDetail, UserError> result, BuildContext context) {
|
||||
result.fold(
|
||||
(user) => router.showHomeScreen(context, user),
|
||||
(error) => _showErrorMessage(context, error.msg),
|
||||
);
|
||||
}
|
||||
|
||||
@ -51,22 +48,13 @@ class SignInScreen extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showHomeScreen(BuildContext context, UserDetail userDetail) {
|
||||
Navigator.pushReplacement(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return HomeScreen(userDetail);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SignInForm extends StatelessWidget {
|
||||
final IAuthRouter router;
|
||||
const SignInForm({
|
||||
Key? key,
|
||||
required this.router,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -80,57 +68,12 @@ class SignInForm extends StatelessWidget {
|
||||
logoSize: Size(60, 60),
|
||||
),
|
||||
const VSpace(30),
|
||||
RoundedInputField(
|
||||
hintText: 'email',
|
||||
onChanged: (value) =>
|
||||
context.read<SignInBloc>().add(SignInEvent.emailChanged(value)),
|
||||
),
|
||||
RoundedInputField(
|
||||
obscureText: true,
|
||||
hintText: 'password',
|
||||
onChanged: (value) => context
|
||||
.read<SignInBloc>()
|
||||
.add(SignInEvent.passwordChanged(value)),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: const TextStyle(fontSize: 12),
|
||||
),
|
||||
onPressed: () => _showForgetPasswordScreen(context),
|
||||
child: const Text(
|
||||
'Forgot Password?',
|
||||
style: TextStyle(color: Colors.lightBlue),
|
||||
),
|
||||
),
|
||||
RoundedButton(
|
||||
title: 'Login',
|
||||
height: 60,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.lightBlue,
|
||||
press: () {
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInWithUserEmailAndPassword());
|
||||
},
|
||||
),
|
||||
const EmailTextField(),
|
||||
const PasswordTextField(),
|
||||
ForgetPasswordButton(router: router),
|
||||
const LoginButton(),
|
||||
const VSpace(10),
|
||||
Row(
|
||||
children: [
|
||||
const Text("Dont't have an account",
|
||||
style: TextStyle(color: Colors.blueGrey, fontSize: 12)),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: const TextStyle(fontSize: 12),
|
||||
),
|
||||
onPressed: () {},
|
||||
child: const Text(
|
||||
'Sign Up',
|
||||
style: TextStyle(color: Colors.lightBlue),
|
||||
),
|
||||
),
|
||||
],
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
),
|
||||
SignUpPrompt(router: router),
|
||||
if (context.read<SignInBloc>().state.isSubmitting) ...[
|
||||
const SizedBox(height: 8),
|
||||
const LinearProgressIndicator(value: null),
|
||||
@ -139,8 +82,136 @@ class SignInForm extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _showForgetPasswordScreen(BuildContext context) {
|
||||
throw UnimplementedError();
|
||||
class SignUpPrompt extends StatelessWidget {
|
||||
const SignUpPrompt({
|
||||
Key? key,
|
||||
required this.router,
|
||||
}) : super(key: key);
|
||||
|
||||
final IAuthRouter router;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Row(
|
||||
children: [
|
||||
const Text("Dont't have an account",
|
||||
style: TextStyle(color: Colors.blueGrey, fontSize: 12)),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: const TextStyle(fontSize: 12),
|
||||
),
|
||||
onPressed: () => router.showSignUpScreen(context),
|
||||
child: const Text(
|
||||
'Sign Up',
|
||||
style: TextStyle(color: Colors.lightBlue),
|
||||
),
|
||||
),
|
||||
],
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class LoginButton extends StatelessWidget {
|
||||
const LoginButton({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RoundedButton(
|
||||
title: 'Login',
|
||||
height: 65,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
color: Colors.lightBlue,
|
||||
press: () {
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInWithUserEmailAndPassword());
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ForgetPasswordButton extends StatelessWidget {
|
||||
const ForgetPasswordButton({
|
||||
Key? key,
|
||||
required this.router,
|
||||
}) : super(key: key);
|
||||
|
||||
final IAuthRouter router;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
textStyle: const TextStyle(fontSize: 12),
|
||||
),
|
||||
onPressed: () => router.showForgetPasswordScreen(context),
|
||||
child: const Text(
|
||||
'Forgot Password?',
|
||||
style: TextStyle(color: Colors.lightBlue),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class PasswordTextField extends StatelessWidget {
|
||||
const PasswordTextField({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignInBloc, SignInState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.passwordError != current.passwordError,
|
||||
builder: (context, state) {
|
||||
return RoundedInputField(
|
||||
obscureText: true,
|
||||
hintText: 'password',
|
||||
normalBorderColor: Colors.green,
|
||||
highlightBorderColor: Colors.red,
|
||||
errorText: context
|
||||
.read<SignInBloc>()
|
||||
.state
|
||||
.passwordError
|
||||
.fold(() => "", (error) => error),
|
||||
onChanged: (value) => context
|
||||
.read<SignInBloc>()
|
||||
.add(SignInEvent.passwordChanged(value)),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EmailTextField extends StatelessWidget {
|
||||
const EmailTextField({
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<SignInBloc, SignInState>(
|
||||
buildWhen: (previous, current) =>
|
||||
previous.emailError != current.emailError,
|
||||
builder: (context, state) {
|
||||
return RoundedInputField(
|
||||
hintText: 'email',
|
||||
normalBorderColor: Colors.green,
|
||||
highlightBorderColor: Colors.red,
|
||||
errorText: context
|
||||
.read<SignInBloc>()
|
||||
.state
|
||||
.emailError
|
||||
.fold(() => "", (error) => error),
|
||||
onChanged: (value) =>
|
||||
context.read<SignInBloc>().add(SignInEvent.emailChanged(value)),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:app_flowy/startup/startup.dart';
|
||||
import 'package:app_flowy/user/domain/i_auth.dart';
|
||||
import 'package:app_flowy/user/presentation/sign_in/sign_in_screen.dart';
|
||||
import 'package:app_flowy/welcome/domain/auth_state.dart';
|
||||
import 'package:app_flowy/welcome/domain/i_welcome.dart';
|
||||
@ -34,6 +36,6 @@ class WelcomeRoute implements IWelcomeRoute {
|
||||
|
||||
@override
|
||||
Widget pushSignInScreen() {
|
||||
return const SignInScreen();
|
||||
return SignInScreen(router: getIt<IAuthRouter>());
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,14 @@
|
||||
import 'package:flowy_infra_ui/widget/text_field_container.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flowy_infra/time/duration.dart';
|
||||
|
||||
class RoundedInputField extends StatelessWidget {
|
||||
final String? hintText;
|
||||
final IconData? icon;
|
||||
final bool obscureText;
|
||||
final Color normalBorderColor;
|
||||
final Color highlightBorderColor;
|
||||
final String errorText;
|
||||
final ValueChanged<String>? onChanged;
|
||||
|
||||
const RoundedInputField({
|
||||
@ -13,6 +17,9 @@ class RoundedInputField extends StatelessWidget {
|
||||
this.icon,
|
||||
this.obscureText = false,
|
||||
this.onChanged,
|
||||
this.normalBorderColor = Colors.transparent,
|
||||
this.highlightBorderColor = Colors.transparent,
|
||||
this.errorText = "",
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
@ -24,19 +31,38 @@ class RoundedInputField extends StatelessWidget {
|
||||
color: const Color(0xFF6F35A5),
|
||||
);
|
||||
|
||||
return TextFieldContainer(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderColor: Colors.blueGrey,
|
||||
child: TextFormField(
|
||||
onChanged: onChanged,
|
||||
cursorColor: const Color(0xFF6F35A5),
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
icon: newIcon,
|
||||
hintText: hintText,
|
||||
border: InputBorder.none,
|
||||
var borderColor = normalBorderColor;
|
||||
if (errorText.isNotEmpty) {
|
||||
borderColor = highlightBorderColor;
|
||||
}
|
||||
|
||||
List<Widget> children = [
|
||||
TextFieldContainer(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
borderColor: borderColor,
|
||||
child: TextFormField(
|
||||
onChanged: onChanged,
|
||||
cursorColor: const Color(0xFF6F35A5),
|
||||
obscureText: obscureText,
|
||||
decoration: InputDecoration(
|
||||
icon: newIcon,
|
||||
hintText: hintText,
|
||||
border: InputBorder.none,
|
||||
),
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
if (errorText.isNotEmpty) {
|
||||
children
|
||||
.add(Text(errorText, style: TextStyle(color: highlightBorderColor)));
|
||||
}
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: .3.seconds,
|
||||
child: Column(
|
||||
children: children,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user