sign_up_screen.dart 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  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:flowy_infra/size.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-user/errors.pb.dart';
  11. import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
  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. class SignUpScreen extends StatelessWidget {
  18. final IAuthRouter router;
  19. const SignUpScreen({Key? key, required this.router}) : super(key: key);
  20. @override
  21. Widget build(BuildContext context) {
  22. return BlocProvider(
  23. create: (context) => getIt<SignUpBloc>(),
  24. child: BlocListener<SignUpBloc, SignUpState>(
  25. listener: (context, state) {
  26. state.successOrFail.fold(
  27. () => {},
  28. (result) => _handleSuccessOrFail(context, result),
  29. );
  30. },
  31. child: const Scaffold(body: SignUpForm()),
  32. ),
  33. );
  34. }
  35. void _handleSuccessOrFail(BuildContext context, Either<UserProfile, UserError> result) {
  36. result.fold(
  37. (user) => router.pushWelcomeScreen(context, user),
  38. (error) => showSnapBar(context, error.msg),
  39. );
  40. }
  41. }
  42. class SignUpForm extends StatelessWidget {
  43. const SignUpForm({
  44. Key? key,
  45. }) : super(key: key);
  46. @override
  47. Widget build(BuildContext context) {
  48. return Align(
  49. alignment: Alignment.center,
  50. child: AuthFormContainer(
  51. children: [
  52. const AuthFormTitle(
  53. title: 'Sign Up to Appflowy',
  54. logoSize: Size(60, 60),
  55. ),
  56. const VSpace(30),
  57. const EmailTextField(),
  58. const PasswordTextField(),
  59. const RepeatPasswordTextField(),
  60. const VSpace(30),
  61. const SignUpButton(),
  62. const VSpace(10),
  63. const SignUpPrompt(),
  64. if (context.read<SignUpBloc>().state.isSubmitting) ...[
  65. const SizedBox(height: 8),
  66. const LinearProgressIndicator(value: null),
  67. ]
  68. ],
  69. ),
  70. );
  71. }
  72. }
  73. class SignUpPrompt extends StatelessWidget {
  74. const SignUpPrompt({
  75. Key? key,
  76. }) : super(key: key);
  77. @override
  78. Widget build(BuildContext context) {
  79. final theme = context.watch<AppTheme>();
  80. return Row(
  81. children: [
  82. Text(
  83. "Already have an account?",
  84. style: TextStyle(color: theme.shader3, fontSize: 12),
  85. ),
  86. TextButton(
  87. style: TextButton.styleFrom(textStyle: const TextStyle(fontSize: 12)),
  88. onPressed: () => Navigator.pop(context),
  89. child: Text('Sign In', style: TextStyle(color: theme.main1)),
  90. ),
  91. ],
  92. mainAxisAlignment: MainAxisAlignment.center,
  93. );
  94. }
  95. }
  96. class SignUpButton extends StatelessWidget {
  97. const SignUpButton({
  98. Key? key,
  99. }) : super(key: key);
  100. @override
  101. Widget build(BuildContext context) {
  102. final theme = context.watch<AppTheme>();
  103. return RoundedTextButton(
  104. title: 'Get Started',
  105. height: 48,
  106. color: theme.main1,
  107. onPressed: () {
  108. context.read<SignUpBloc>().add(const SignUpEvent.signUpWithUserEmailAndPassword());
  109. },
  110. );
  111. }
  112. }
  113. class PasswordTextField extends StatelessWidget {
  114. const PasswordTextField({
  115. Key? key,
  116. }) : super(key: key);
  117. @override
  118. Widget build(BuildContext context) {
  119. final theme = context.watch<AppTheme>();
  120. return BlocBuilder<SignUpBloc, SignUpState>(
  121. buildWhen: (previous, current) => previous.passwordError != current.passwordError,
  122. builder: (context, state) {
  123. return RoundedInputField(
  124. obscureText: true,
  125. obscureIcon: svg("home/Hide"),
  126. obscureHideIcon: svg("home/Show"),
  127. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  128. hintText: "Password",
  129. normalBorderColor: theme.shader4,
  130. highlightBorderColor: theme.red,
  131. cursorColor: theme.main1,
  132. errorText: context.read<SignUpBloc>().state.passwordError.fold(() => "", (error) => error),
  133. onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.passwordChanged(value)),
  134. );
  135. },
  136. );
  137. }
  138. }
  139. class RepeatPasswordTextField extends StatelessWidget {
  140. const RepeatPasswordTextField({
  141. Key? key,
  142. }) : super(key: key);
  143. @override
  144. Widget build(BuildContext context) {
  145. final theme = context.watch<AppTheme>();
  146. return BlocBuilder<SignUpBloc, SignUpState>(
  147. buildWhen: (previous, current) => previous.repeatPasswordError != current.repeatPasswordError,
  148. builder: (context, state) {
  149. return RoundedInputField(
  150. obscureText: true,
  151. obscureIcon: svg("home/Hide"),
  152. obscureHideIcon: svg("home/Show"),
  153. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  154. hintText: "Repeate password",
  155. normalBorderColor: theme.shader4,
  156. highlightBorderColor: theme.red,
  157. cursorColor: theme.main1,
  158. errorText: context.read<SignUpBloc>().state.repeatPasswordError.fold(() => "", (error) => error),
  159. onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.repeatPasswordChanged(value)),
  160. );
  161. },
  162. );
  163. }
  164. }
  165. class EmailTextField extends StatelessWidget {
  166. const EmailTextField({
  167. Key? key,
  168. }) : super(key: key);
  169. @override
  170. Widget build(BuildContext context) {
  171. final theme = context.watch<AppTheme>();
  172. return BlocBuilder<SignUpBloc, SignUpState>(
  173. buildWhen: (previous, current) => previous.emailError != current.emailError,
  174. builder: (context, state) {
  175. return RoundedInputField(
  176. hintText: 'Email',
  177. style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w500),
  178. normalBorderColor: theme.shader4,
  179. highlightBorderColor: theme.red,
  180. cursorColor: theme.main1,
  181. errorText: context.read<SignUpBloc>().state.emailError.fold(() => "", (error) => error),
  182. onChanged: (value) => context.read<SignUpBloc>().add(SignUpEvent.emailChanged(value)),
  183. );
  184. },
  185. );
  186. }
  187. }