sign_up_screen.dart 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. import 'package:appflowy/startup/startup.dart';
  2. import 'package:appflowy/user/application/sign_up_bloc.dart';
  3. import 'package:appflowy/user/presentation/router.dart';
  4. import 'package:appflowy/user/presentation/widgets/background.dart';
  5. import 'package:easy_localization/easy_localization.dart';
  6. import 'package:flowy_infra_ui/style_widget/text.dart';
  7. import 'package:flowy_infra_ui/widget/rounded_button.dart';
  8. import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
  9. import 'package:flowy_infra_ui/widget/spacing.dart';
  10. import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
  11. import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
  12. show UserProfilePB;
  13. import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
  14. import 'package:flutter/material.dart';
  15. import 'package:flutter_bloc/flutter_bloc.dart';
  16. import 'package:dartz/dartz.dart';
  17. import 'package:flowy_infra/image.dart';
  18. import 'package:appflowy/generated/locale_keys.g.dart';
  19. class SignUpScreen extends StatelessWidget {
  20. final AuthRouter router;
  21. const SignUpScreen({Key? key, required this.router}) : super(key: key);
  22. @override
  23. Widget build(BuildContext context) {
  24. return BlocProvider(
  25. create: (context) => getIt<SignUpBloc>(),
  26. child: BlocListener<SignUpBloc, SignUpState>(
  27. listener: (context, state) {
  28. state.successOrFail.fold(
  29. () => {},
  30. (result) => _handleSuccessOrFail(context, result),
  31. );
  32. },
  33. child: const Scaffold(body: SignUpForm()),
  34. ),
  35. );
  36. }
  37. void _handleSuccessOrFail(
  38. BuildContext context,
  39. Either<UserProfilePB, FlowyError> result,
  40. ) {
  41. result.fold(
  42. (user) => router.pushWelcomeScreen(context, user),
  43. (error) => showSnapBar(context, error.msg),
  44. );
  45. }
  46. }
  47. class SignUpForm extends StatelessWidget {
  48. const SignUpForm({
  49. Key? key,
  50. }) : super(key: key);
  51. @override
  52. Widget build(BuildContext context) {
  53. return Align(
  54. alignment: Alignment.center,
  55. child: AuthFormContainer(
  56. children: [
  57. FlowyLogoTitle(
  58. title: LocaleKeys.signUp_title.tr(),
  59. logoSize: const Size(60, 60),
  60. ),
  61. const VSpace(30),
  62. const EmailTextField(),
  63. const PasswordTextField(),
  64. const RepeatPasswordTextField(),
  65. const VSpace(30),
  66. const SignUpButton(),
  67. const VSpace(10),
  68. const SignUpPrompt(),
  69. if (context.read<SignUpBloc>().state.isSubmitting) ...[
  70. const SizedBox(height: 8),
  71. const LinearProgressIndicator(value: null),
  72. ]
  73. ],
  74. ),
  75. );
  76. }
  77. }
  78. class SignUpPrompt extends StatelessWidget {
  79. const SignUpPrompt({
  80. Key? key,
  81. }) : super(key: key);
  82. @override
  83. Widget build(BuildContext context) {
  84. return Row(
  85. mainAxisAlignment: MainAxisAlignment.center,
  86. children: [
  87. FlowyText.medium(
  88. LocaleKeys.signUp_alreadyHaveAnAccount.tr(),
  89. color: Theme.of(context).hintColor,
  90. ),
  91. TextButton(
  92. style: TextButton.styleFrom(
  93. textStyle: Theme.of(context).textTheme.bodyMedium,
  94. ),
  95. onPressed: () => Navigator.pop(context),
  96. child: FlowyText.medium(
  97. LocaleKeys.signIn_buttonText.tr(),
  98. color: Theme.of(context).colorScheme.primary,
  99. ),
  100. ),
  101. ],
  102. );
  103. }
  104. }
  105. class SignUpButton extends StatelessWidget {
  106. const SignUpButton({
  107. Key? key,
  108. }) : super(key: key);
  109. @override
  110. Widget build(BuildContext context) {
  111. return RoundedTextButton(
  112. title: LocaleKeys.signUp_getStartedText.tr(),
  113. height: 48,
  114. onPressed: () {
  115. context
  116. .read<SignUpBloc>()
  117. .add(const SignUpEvent.signUpWithUserEmailAndPassword());
  118. },
  119. );
  120. }
  121. }
  122. class PasswordTextField extends StatelessWidget {
  123. const PasswordTextField({
  124. Key? key,
  125. }) : super(key: key);
  126. @override
  127. Widget build(BuildContext context) {
  128. return BlocBuilder<SignUpBloc, SignUpState>(
  129. buildWhen: (previous, current) =>
  130. previous.passwordError != current.passwordError,
  131. builder: (context, state) {
  132. return RoundedInputField(
  133. obscureText: true,
  134. obscureIcon: svgWidget("home/hide"),
  135. obscureHideIcon: svgWidget("home/show"),
  136. hintText: LocaleKeys.signUp_passwordHint.tr(),
  137. normalBorderColor: Theme.of(context).colorScheme.outline,
  138. errorBorderColor: Theme.of(context).colorScheme.error,
  139. cursorColor: Theme.of(context).colorScheme.primary,
  140. errorText: context
  141. .read<SignUpBloc>()
  142. .state
  143. .passwordError
  144. .fold(() => "", (error) => error),
  145. onChanged: (value) => context
  146. .read<SignUpBloc>()
  147. .add(SignUpEvent.passwordChanged(value)),
  148. );
  149. },
  150. );
  151. }
  152. }
  153. class RepeatPasswordTextField extends StatelessWidget {
  154. const RepeatPasswordTextField({
  155. Key? key,
  156. }) : super(key: key);
  157. @override
  158. Widget build(BuildContext context) {
  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: svgWidget("home/hide"),
  166. obscureHideIcon: svgWidget("home/show"),
  167. hintText: LocaleKeys.signUp_repeatPasswordHint.tr(),
  168. normalBorderColor: Theme.of(context).colorScheme.outline,
  169. errorBorderColor: Theme.of(context).colorScheme.error,
  170. cursorColor: Theme.of(context).colorScheme.primary,
  171. errorText: context
  172. .read<SignUpBloc>()
  173. .state
  174. .repeatPasswordError
  175. .fold(() => "", (error) => error),
  176. onChanged: (value) => context
  177. .read<SignUpBloc>()
  178. .add(SignUpEvent.repeatPasswordChanged(value)),
  179. );
  180. },
  181. );
  182. }
  183. }
  184. class EmailTextField extends StatelessWidget {
  185. const EmailTextField({
  186. Key? key,
  187. }) : super(key: key);
  188. @override
  189. Widget build(BuildContext context) {
  190. return BlocBuilder<SignUpBloc, SignUpState>(
  191. buildWhen: (previous, current) =>
  192. previous.emailError != current.emailError,
  193. builder: (context, state) {
  194. return RoundedInputField(
  195. hintText: LocaleKeys.signUp_emailHint.tr(),
  196. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  197. normalBorderColor: Theme.of(context).colorScheme.outline,
  198. errorBorderColor: Theme.of(context).colorScheme.error,
  199. cursorColor: Theme.of(context).colorScheme.primary,
  200. errorText: context
  201. .read<SignUpBloc>()
  202. .state
  203. .emailError
  204. .fold(() => "", (error) => error),
  205. onChanged: (value) =>
  206. context.read<SignUpBloc>().add(SignUpEvent.emailChanged(value)),
  207. );
  208. },
  209. );
  210. }
  211. }