sign_up_screen.dart 7.1 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/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_ui/widget/rounded_button.dart';
  9. import 'package:flowy_infra_ui/widget/rounded_input_field.dart';
  10. import 'package:flowy_infra_ui/widget/spacing.dart';
  11. import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
  12. import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' 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:app_flowy/generated/locale_keys.g.dart';
  19. import 'package:textstyle_extensions/textstyle_extensions.dart';
  20. class SignUpScreen extends StatelessWidget {
  21. final AuthRouter router;
  22. const SignUpScreen({Key? key, required this.router}) : super(key: key);
  23. @override
  24. Widget build(BuildContext context) {
  25. return BlocProvider(
  26. create: (context) => getIt<SignUpBloc>(),
  27. child: BlocListener<SignUpBloc, SignUpState>(
  28. listener: (context, state) {
  29. state.successOrFail.fold(
  30. () => {},
  31. (result) => _handleSuccessOrFail(context, result),
  32. );
  33. },
  34. child: const Scaffold(body: SignUpForm()),
  35. ),
  36. );
  37. }
  38. void _handleSuccessOrFail(
  39. BuildContext context, Either<UserProfilePB, FlowyError> result) {
  40. result.fold(
  41. (user) => router.pushWelcomeScreen(context, user),
  42. (error) => showSnapBar(context, error.msg),
  43. );
  44. }
  45. }
  46. class SignUpForm extends StatelessWidget {
  47. const SignUpForm({
  48. Key? key,
  49. }) : super(key: key);
  50. @override
  51. Widget build(BuildContext context) {
  52. return Align(
  53. alignment: Alignment.center,
  54. child: AuthFormContainer(
  55. children: [
  56. FlowyLogoTitle(
  57. title: LocaleKeys.signUp_title.tr(),
  58. logoSize: const Size(60, 60),
  59. ),
  60. const VSpace(30),
  61. const EmailTextField(),
  62. const PasswordTextField(),
  63. const RepeatPasswordTextField(),
  64. const VSpace(30),
  65. const SignUpButton(),
  66. const VSpace(10),
  67. const SignUpPrompt(),
  68. if (context.read<SignUpBloc>().state.isSubmitting) ...[
  69. const SizedBox(height: 8),
  70. const LinearProgressIndicator(value: null),
  71. ]
  72. ],
  73. ),
  74. );
  75. }
  76. }
  77. class SignUpPrompt extends StatelessWidget {
  78. const SignUpPrompt({
  79. Key? key,
  80. }) : super(key: key);
  81. @override
  82. Widget build(BuildContext context) {
  83. return Row(
  84. mainAxisAlignment: MainAxisAlignment.center,
  85. children: [
  86. Text(
  87. LocaleKeys.signUp_alreadyHaveAnAccount.tr(),
  88. style: TextStyle(color: Theme.of(context).hintColor, fontSize: 12),
  89. ),
  90. TextButton(
  91. style: TextButton.styleFrom(textStyle: TextStyles.body1),
  92. onPressed: () => Navigator.pop(context),
  93. child: Text(LocaleKeys.signIn_buttonText.tr(),
  94. style: TextStyle(color: Theme.of(context).colorScheme.primary)),
  95. ),
  96. ],
  97. );
  98. }
  99. }
  100. class SignUpButton extends StatelessWidget {
  101. const SignUpButton({
  102. Key? key,
  103. }) : super(key: key);
  104. @override
  105. Widget build(BuildContext context) {
  106. return RoundedTextButton(
  107. title: LocaleKeys.signUp_getStartedText.tr(),
  108. height: 48,
  109. color: Theme.of(context).colorScheme.primary,
  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. style: TextStyles.body1.size(FontSizes.s14),
  133. hintText: LocaleKeys.signUp_passwordHint.tr(),
  134. normalBorderColor: Theme.of(context).colorScheme.outline,
  135. errorBorderColor: Theme.of(context).colorScheme.error,
  136. cursorColor: Theme.of(context).colorScheme.primary,
  137. errorText: context
  138. .read<SignUpBloc>()
  139. .state
  140. .passwordError
  141. .fold(() => "", (error) => error),
  142. onChanged: (value) => context
  143. .read<SignUpBloc>()
  144. .add(SignUpEvent.passwordChanged(value)),
  145. );
  146. },
  147. );
  148. }
  149. }
  150. class RepeatPasswordTextField extends StatelessWidget {
  151. const RepeatPasswordTextField({
  152. Key? key,
  153. }) : super(key: key);
  154. @override
  155. Widget build(BuildContext context) {
  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: svgWidget("home/hide"),
  163. obscureHideIcon: svgWidget("home/show"),
  164. style: TextStyles.body1.size(FontSizes.s14),
  165. hintText: LocaleKeys.signUp_repeatPasswordHint.tr(),
  166. normalBorderColor: Theme.of(context).colorScheme.outline,
  167. errorBorderColor: Theme.of(context).colorScheme.error,
  168. cursorColor: Theme.of(context).colorScheme.primary,
  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. return BlocBuilder<SignUpBloc, SignUpState>(
  189. buildWhen: (previous, current) =>
  190. previous.emailError != current.emailError,
  191. builder: (context, state) {
  192. return RoundedInputField(
  193. hintText: LocaleKeys.signUp_emailHint.tr(),
  194. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  195. normalBorderColor: Theme.of(context).colorScheme.outline,
  196. errorBorderColor: Theme.of(context).colorScheme.error,
  197. cursorColor: Theme.of(context).colorScheme.primary,
  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. }