sign_up_screen.dart 6.9 KB

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