sign_up_screen.dart 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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/size.dart';
  7. import 'package:flowy_infra/text_style.dart';
  8. import 'package:flowy_infra/theme.dart';
  9. import 'package:flowy_infra_ui/widget/rounded_button.dart';
  10. import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
  11. import 'package:flowy_infra_ui/widget/spacing.dart';
  12. import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
  13. import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
  14. import 'package:flowy_infra_ui/style_widget/snap_bar.dart';
  15. import 'package:flutter/material.dart';
  16. import 'package:flutter_bloc/flutter_bloc.dart';
  17. import 'package:dartz/dartz.dart';
  18. import 'package:flowy_infra/image.dart';
  19. import 'package:app_flowy/generated/locale_keys.g.dart';
  20. import 'package:textstyle_extensions/textstyle_extensions.dart';
  21. class SignUpScreen extends StatelessWidget {
  22. final AuthRouter router;
  23. const SignUpScreen({Key? key, required this.router}) : super(key: key);
  24. @override
  25. Widget build(BuildContext context) {
  26. return BlocProvider(
  27. create: (context) => getIt<SignUpBloc>(),
  28. child: BlocListener<SignUpBloc, SignUpState>(
  29. listener: (context, state) {
  30. state.successOrFail.fold(
  31. () => {},
  32. (result) => _handleSuccessOrFail(context, result),
  33. );
  34. },
  35. child: const Scaffold(body: SignUpForm()),
  36. ),
  37. );
  38. }
  39. void _handleSuccessOrFail(
  40. BuildContext context, Either<UserProfilePB, FlowyError> result) {
  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. final theme = context.watch<AppTheme>();
  85. return Row(
  86. mainAxisAlignment: MainAxisAlignment.center,
  87. children: [
  88. Text(
  89. LocaleKeys.signUp_alreadyHaveAnAccount.tr(),
  90. style: TextStyle(color: theme.shader3, fontSize: 12),
  91. ),
  92. TextButton(
  93. style: TextButton.styleFrom(textStyle: TextStyles.body1),
  94. onPressed: () => Navigator.pop(context),
  95. child: Text(LocaleKeys.signIn_buttonText.tr(),
  96. style: TextStyle(color: theme.main1)),
  97. ),
  98. ],
  99. );
  100. }
  101. }
  102. class SignUpButton extends StatelessWidget {
  103. const SignUpButton({
  104. Key? key,
  105. }) : super(key: key);
  106. @override
  107. Widget build(BuildContext context) {
  108. final theme = context.watch<AppTheme>();
  109. return RoundedTextButton(
  110. title: LocaleKeys.signUp_getStartedText.tr(),
  111. height: 48,
  112. color: theme.main1,
  113. onPressed: () {
  114. context
  115. .read<SignUpBloc>()
  116. .add(const SignUpEvent.signUpWithUserEmailAndPassword());
  117. },
  118. );
  119. }
  120. }
  121. class PasswordTextField extends StatelessWidget {
  122. const PasswordTextField({
  123. Key? key,
  124. }) : super(key: key);
  125. @override
  126. Widget build(BuildContext context) {
  127. final theme = context.watch<AppTheme>();
  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. style: TextStyles.body1.size(FontSizes.s14),
  137. hintText: LocaleKeys.signUp_passwordHint.tr(),
  138. normalBorderColor: theme.shader4,
  139. errorBorderColor: theme.red,
  140. cursorColor: theme.main1,
  141. errorText: context
  142. .read<SignUpBloc>()
  143. .state
  144. .passwordError
  145. .fold(() => "", (error) => error),
  146. onChanged: (value) => context
  147. .read<SignUpBloc>()
  148. .add(SignUpEvent.passwordChanged(value)),
  149. );
  150. },
  151. );
  152. }
  153. }
  154. class RepeatPasswordTextField extends StatelessWidget {
  155. const RepeatPasswordTextField({
  156. Key? key,
  157. }) : super(key: key);
  158. @override
  159. Widget build(BuildContext context) {
  160. final theme = context.watch<AppTheme>();
  161. return BlocBuilder<SignUpBloc, SignUpState>(
  162. buildWhen: (previous, current) =>
  163. previous.repeatPasswordError != current.repeatPasswordError,
  164. builder: (context, state) {
  165. return RoundedInputField(
  166. obscureText: true,
  167. obscureIcon: svgWidget("home/hide"),
  168. obscureHideIcon: svgWidget("home/show"),
  169. style: TextStyles.body1.size(FontSizes.s14),
  170. hintText: LocaleKeys.signUp_repeatPasswordHint.tr(),
  171. normalBorderColor: theme.shader4,
  172. errorBorderColor: theme.red,
  173. cursorColor: theme.main1,
  174. errorText: context
  175. .read<SignUpBloc>()
  176. .state
  177. .repeatPasswordError
  178. .fold(() => "", (error) => error),
  179. onChanged: (value) => context
  180. .read<SignUpBloc>()
  181. .add(SignUpEvent.repeatPasswordChanged(value)),
  182. );
  183. },
  184. );
  185. }
  186. }
  187. class EmailTextField extends StatelessWidget {
  188. const EmailTextField({
  189. Key? key,
  190. }) : super(key: key);
  191. @override
  192. Widget build(BuildContext context) {
  193. final theme = context.watch<AppTheme>();
  194. return BlocBuilder<SignUpBloc, SignUpState>(
  195. buildWhen: (previous, current) =>
  196. previous.emailError != current.emailError,
  197. builder: (context, state) {
  198. return RoundedInputField(
  199. hintText: LocaleKeys.signUp_emailHint.tr(),
  200. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  201. normalBorderColor: theme.shader4,
  202. errorBorderColor: theme.red,
  203. cursorColor: theme.main1,
  204. errorText: context
  205. .read<SignUpBloc>()
  206. .state
  207. .emailError
  208. .fold(() => "", (error) => error),
  209. onChanged: (value) =>
  210. context.read<SignUpBloc>().add(SignUpEvent.emailChanged(value)),
  211. );
  212. },
  213. );
  214. }
  215. }