sign_up_screen.dart 6.6 KB

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