sign_in_screen.dart 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import 'package:app_flowy/startup/startup.dart';
  2. import 'package:app_flowy/user/application/sign_in_bloc.dart';
  3. import 'package:app_flowy/user/domain/i_auth.dart';
  4. import 'package:app_flowy/user/presentation/widgets/background.dart';
  5. import 'package:flowy_infra/theme.dart';
  6. import 'package:flowy_infra_ui/widget/rounded_button.dart';
  7. import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
  8. import 'package:flowy_infra_ui/widget/spacing.dart';
  9. import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
  10. import 'package:flowy_sdk/protobuf/flowy-user/errors.pb.dart';
  11. import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
  12. import 'package:flutter/material.dart';
  13. import 'package:flutter_bloc/flutter_bloc.dart';
  14. import 'package:dartz/dartz.dart';
  15. import 'package:flowy_infra/image.dart';
  16. class SignInScreen extends StatelessWidget {
  17. final IAuthRouter router;
  18. const SignInScreen({Key? key, required this.router}) : super(key: key);
  19. @override
  20. Widget build(BuildContext context) {
  21. return BlocProvider(
  22. create: (context) => getIt<SignInBloc>(),
  23. child: BlocListener<SignInBloc, SignInState>(
  24. listener: (context, state) {
  25. state.successOrFail.fold(
  26. () => null,
  27. (result) => _handleSuccessOrFail(result, context),
  28. );
  29. },
  30. child: Scaffold(
  31. body: SignInForm(router: router),
  32. ),
  33. ),
  34. );
  35. }
  36. void _handleSuccessOrFail(
  37. Either<UserProfile, UserError> result, BuildContext context) {
  38. result.fold(
  39. (user) => router.pushWelcomeScreen(context, user),
  40. (error) => showSnapBar(context, error.msg),
  41. );
  42. }
  43. }
  44. class SignInForm extends StatelessWidget {
  45. final IAuthRouter router;
  46. const SignInForm({
  47. Key? key,
  48. required this.router,
  49. }) : super(key: key);
  50. @override
  51. Widget build(BuildContext context) {
  52. return Align(
  53. alignment: Alignment.center,
  54. child: AuthFormContainer(
  55. children: [
  56. const AuthFormTitle(
  57. title: 'Login to Appflowy',
  58. logoSize: Size(60, 60),
  59. ),
  60. const VSpace(30),
  61. const EmailTextField(),
  62. const PasswordTextField(),
  63. ForgetPasswordButton(router: router),
  64. const VSpace(30),
  65. const LoginButton(),
  66. const VSpace(10),
  67. SignUpPrompt(router: router),
  68. if (context.read<SignInBloc>().state.isSubmitting) ...[
  69. const SizedBox(height: 8),
  70. const LinearProgressIndicator(value: null),
  71. ]
  72. ],
  73. ),
  74. );
  75. }
  76. }
  77. class SignUpPrompt extends StatelessWidget {
  78. const SignUpPrompt({
  79. Key? key,
  80. required this.router,
  81. }) : super(key: key);
  82. final IAuthRouter router;
  83. @override
  84. Widget build(BuildContext context) {
  85. final theme = context.watch<AppTheme>();
  86. return Row(
  87. children: [
  88. Text("Dont't have an account",
  89. style: TextStyle(color: theme.shader3, fontSize: 12)),
  90. TextButton(
  91. style: TextButton.styleFrom(
  92. textStyle: const TextStyle(fontSize: 12),
  93. ),
  94. onPressed: () => router.pushSignUpScreen(context),
  95. child: Text(
  96. 'Sign Up',
  97. style: TextStyle(color: theme.main1),
  98. ),
  99. ),
  100. ],
  101. mainAxisAlignment: MainAxisAlignment.center,
  102. );
  103. }
  104. }
  105. class LoginButton extends StatelessWidget {
  106. const LoginButton({
  107. Key? key,
  108. }) : super(key: key);
  109. @override
  110. Widget build(BuildContext context) {
  111. final theme = context.watch<AppTheme>();
  112. return RoundedTextButton(
  113. title: 'Login',
  114. height: 48,
  115. borderRadius: BorderRadius.circular(10),
  116. color: theme.main1,
  117. press: () {
  118. context
  119. .read<SignInBloc>()
  120. .add(const SignInEvent.signedInWithUserEmailAndPassword());
  121. },
  122. );
  123. }
  124. }
  125. class ForgetPasswordButton extends StatelessWidget {
  126. const ForgetPasswordButton({
  127. Key? key,
  128. required this.router,
  129. }) : super(key: key);
  130. final IAuthRouter router;
  131. @override
  132. Widget build(BuildContext context) {
  133. final theme = context.watch<AppTheme>();
  134. return TextButton(
  135. style: TextButton.styleFrom(
  136. textStyle: const TextStyle(fontSize: 12),
  137. ),
  138. onPressed: () => router.pushForgetPasswordScreen(context),
  139. child: Text(
  140. 'Forgot Password?',
  141. style: TextStyle(color: theme.main1),
  142. ),
  143. );
  144. }
  145. }
  146. class PasswordTextField extends StatelessWidget {
  147. const PasswordTextField({
  148. Key? key,
  149. }) : super(key: key);
  150. @override
  151. Widget build(BuildContext context) {
  152. final theme = context.watch<AppTheme>();
  153. return BlocBuilder<SignInBloc, SignInState>(
  154. buildWhen: (previous, current) =>
  155. previous.passwordError != current.passwordError,
  156. builder: (context, state) {
  157. return RoundedInputField(
  158. obscureText: true,
  159. fontSize: 14,
  160. obscureIcon: svg("home/Hide"),
  161. obscureHideIcon: svg("home/Show"),
  162. hintText: 'Password',
  163. normalBorderColor: theme.shader4,
  164. highlightBorderColor: theme.red,
  165. errorText: context
  166. .read<SignInBloc>()
  167. .state
  168. .passwordError
  169. .fold(() => "", (error) => error),
  170. onChanged: (value) => context
  171. .read<SignInBloc>()
  172. .add(SignInEvent.passwordChanged(value)),
  173. );
  174. },
  175. );
  176. }
  177. }
  178. class EmailTextField extends StatelessWidget {
  179. const EmailTextField({
  180. Key? key,
  181. }) : super(key: key);
  182. @override
  183. Widget build(BuildContext context) {
  184. final theme = context.watch<AppTheme>();
  185. return BlocBuilder<SignInBloc, SignInState>(
  186. buildWhen: (previous, current) =>
  187. previous.emailError != current.emailError,
  188. builder: (context, state) {
  189. return RoundedInputField(
  190. hintText: 'Email',
  191. fontSize: 14,
  192. normalBorderColor: theme.shader4,
  193. highlightBorderColor: theme.red,
  194. errorText: context
  195. .read<SignInBloc>()
  196. .state
  197. .emailError
  198. .fold(() => "", (error) => error),
  199. onChanged: (value) =>
  200. context.read<SignInBloc>().add(SignInEvent.emailChanged(value)),
  201. );
  202. },
  203. );
  204. }
  205. }