sign_up_screen.dart 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  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/domain/i_auth.dart';
  4. import 'package:app_flowy/user/presentation/widgets/background.dart';
  5. import 'package:easy_localization/easy_localization.dart';
  6. import 'package:flowy_infra/theme.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-data-model/protobuf.dart' show UserProfile;
  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 IAuthRouter 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(BuildContext context, Either<UserProfile, FlowyError> result) {
  37. result.fold(
  38. (user) => router.pushWelcomeScreen(context, user),
  39. (error) => showSnapBar(context, error.msg),
  40. );
  41. }
  42. }
  43. class SignUpForm extends StatelessWidget {
  44. const SignUpForm({
  45. Key? key,
  46. }) : super(key: key);
  47. @override
  48. Widget build(BuildContext context) {
  49. return Align(
  50. alignment: Alignment.center,
  51. child: AuthFormContainer(
  52. children: [
  53. FlowyLogoTitle(
  54. title: LocaleKeys.signUp_title.tr(),
  55. logoSize: const Size(60, 60),
  56. ),
  57. const VSpace(30),
  58. const EmailTextField(),
  59. const PasswordTextField(),
  60. const RepeatPasswordTextField(),
  61. const VSpace(30),
  62. const SignUpButton(),
  63. const VSpace(10),
  64. const SignUpPrompt(),
  65. if (context.read<SignUpBloc>().state.isSubmitting) ...[
  66. const SizedBox(height: 8),
  67. const LinearProgressIndicator(value: null),
  68. ]
  69. ],
  70. ),
  71. );
  72. }
  73. }
  74. class SignUpPrompt extends StatelessWidget {
  75. const SignUpPrompt({
  76. Key? key,
  77. }) : super(key: key);
  78. @override
  79. Widget build(BuildContext context) {
  80. final theme = context.watch<AppTheme>();
  81. return Row(
  82. children: [
  83. Text(
  84. LocaleKeys.signUp_alreadyHaveAnAccount.tr(),
  85. style: TextStyle(color: theme.shader3, fontSize: 12),
  86. ),
  87. TextButton(
  88. style: TextButton.styleFrom(textStyle: const TextStyle(fontSize: 12)),
  89. onPressed: () => Navigator.pop(context),
  90. child: Text(LocaleKeys.signIn_buttonText.tr(), style: TextStyle(color: theme.main1)),
  91. ),
  92. ],
  93. mainAxisAlignment: MainAxisAlignment.center,
  94. );
  95. }
  96. }
  97. class SignUpButton extends StatelessWidget {
  98. const SignUpButton({
  99. Key? key,
  100. }) : super(key: key);
  101. @override
  102. Widget build(BuildContext context) {
  103. final theme = context.watch<AppTheme>();
  104. return RoundedTextButton(
  105. title: LocaleKeys.signUp_getStartedText.tr(),
  106. height: 48,
  107. color: theme.main1,
  108. onPressed: () {
  109. context.read<SignUpBloc>().add(const SignUpEvent.signUpWithUserEmailAndPassword());
  110. },
  111. );
  112. }
  113. }
  114. class PasswordTextField extends StatelessWidget {
  115. const PasswordTextField({
  116. Key? key,
  117. }) : super(key: key);
  118. @override
  119. Widget build(BuildContext context) {
  120. final theme = context.watch<AppTheme>();
  121. return BlocBuilder<SignUpBloc, SignUpState>(
  122. buildWhen: (previous, current) => previous.passwordError != current.passwordError,
  123. builder: (context, state) {
  124. return RoundedInputField(
  125. obscureText: true,
  126. obscureIcon: svg("home/hide"),
  127. obscureHideIcon: svg("home/show"),
  128. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  129. hintText: LocaleKeys.signUp_passwordHint.tr(),
  130. normalBorderColor: theme.shader4,
  131. highlightBorderColor: theme.red,
  132. cursorColor: theme.main1,
  133. errorText: context.read<SignUpBloc>().state.passwordError.fold(() => "", (error) => error),
  134. onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.passwordChanged(value)),
  135. );
  136. },
  137. );
  138. }
  139. }
  140. class RepeatPasswordTextField extends StatelessWidget {
  141. const RepeatPasswordTextField({
  142. Key? key,
  143. }) : super(key: key);
  144. @override
  145. Widget build(BuildContext context) {
  146. final theme = context.watch<AppTheme>();
  147. return BlocBuilder<SignUpBloc, SignUpState>(
  148. buildWhen: (previous, current) => previous.repeatPasswordError != current.repeatPasswordError,
  149. builder: (context, state) {
  150. return RoundedInputField(
  151. obscureText: true,
  152. obscureIcon: svg("home/hide"),
  153. obscureHideIcon: svg("home/show"),
  154. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  155. hintText: LocaleKeys.signUp_repeatPasswordHint.tr(),
  156. normalBorderColor: theme.shader4,
  157. highlightBorderColor: theme.red,
  158. cursorColor: theme.main1,
  159. errorText: context.read<SignUpBloc>().state.repeatPasswordError.fold(() => "", (error) => error),
  160. onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.repeatPasswordChanged(value)),
  161. );
  162. },
  163. );
  164. }
  165. }
  166. class EmailTextField extends StatelessWidget {
  167. const EmailTextField({
  168. Key? key,
  169. }) : super(key: key);
  170. @override
  171. Widget build(BuildContext context) {
  172. final theme = context.watch<AppTheme>();
  173. return BlocBuilder<SignUpBloc, SignUpState>(
  174. buildWhen: (previous, current) => previous.emailError != current.emailError,
  175. builder: (context, state) {
  176. return RoundedInputField(
  177. hintText: LocaleKeys.signUp_emailHint.tr(),
  178. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  179. normalBorderColor: theme.shader4,
  180. highlightBorderColor: theme.red,
  181. cursorColor: theme.main1,
  182. errorText: context.read<SignUpBloc>().state.emailError.fold(() => "", (error) => error),
  183. onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.emailChanged(value)),
  184. );
  185. },
  186. );
  187. }
  188. }