sign_up_screen.dart 6.3 KB

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