sign_up_screen.dart 6.9 KB

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