sign_up_screen.dart 6.5 KB

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