diff --git a/lib/model/MBTIModel.dart b/lib/model/MBTIModel.dart index 1baebfe..4e45d4a 100644 --- a/lib/model/MBTIModel.dart +++ b/lib/model/MBTIModel.dart @@ -7,6 +7,7 @@ part 'generated/MBTIModel.g.dart'; @freezed class MBTIModel with _$MBTIModel { + const MBTIModel._(); const factory MBTIModel({ required Extroversion extroversion, required Sensing sensing, diff --git a/lib/providers/SignUpDataProviders.dart b/lib/providers/SignUpDataProviders.dart new file mode 100644 index 0000000..a7cd463 --- /dev/null +++ b/lib/providers/SignUpDataProviders.dart @@ -0,0 +1,18 @@ +// 이메일 인증 ( 아이디 ) +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final emailProvider = StateProvider((ref) => ''); +final emailVerificationCodeProvider = StateProvider((ref) => ''); + +// 이름, 닉네임 생성 +final nameProvider = StateProvider((ref) => ''); +final nicknameProvider = StateProvider((ref) => ''); + +// 비밀번호 생성 +final passwordProvider = StateProvider((ref) => ''); +final passwordConfirmProvider = StateProvider((ref) => ''); + +// 휴대폰 번호 인증 ( 구매할 때 휴대폰 인증 ) ( 따로 만들기 ) +final residentRegistrationNumberProvider = StateProvider((ref) => ''); +final phoneNumberProvider = StateProvider((ref) => ''); +final verificationNumberProvider = StateProvider((ref) => ''); \ No newline at end of file diff --git a/lib/screens/mypage/MyPageScreen.dart b/lib/screens/mypage/MyPageScreen.dart index 5d62d4d..3d73149 100644 --- a/lib/screens/mypage/MyPageScreen.dart +++ b/lib/screens/mypage/MyPageScreen.dart @@ -1,5 +1,4 @@ import 'dart:io'; - import 'package:blueberry_flutter_template/screens/mypage/camera/setting_inside_account_manager.dart'; import 'package:blueberry_flutter_template/screens/mypage/camera/setting_inside_camera_media.dart'; import 'package:cloud_functions/cloud_functions.dart'; @@ -9,7 +8,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image_picker/image_picker.dart'; - import '../../providers/camera/FirebaseStoreServiceProvider.dart'; import '../../providers/camera/fireStorageServiceProvider.dart'; import '../../providers/user/FirebaseAuthServiceProvider.dart'; @@ -196,14 +194,13 @@ class MyPageScreen extends ConsumerWidget { } } }, - child: const Expanded( - child: ListTile( + child: const ListTile( leading: Icon(Icons.person_off), title: Text( "회원탈퇴", style: TextStyle(fontSize: 20), ), - )), + ), ), ], ), diff --git a/lib/screens/mypage/SignUpScreen.dart b/lib/screens/mypage/SignUpScreen.dart index b649095..f6ab549 100644 --- a/lib/screens/mypage/SignUpScreen.dart +++ b/lib/screens/mypage/SignUpScreen.dart @@ -1,32 +1,18 @@ -import 'package:blueberry_flutter_template/screens/mypage/signup/EmailInputScreen.dart'; -import 'package:blueberry_flutter_template/screens/mypage/signup/PasswordInputPage.dart'; -import 'package:blueberry_flutter_template/screens/mypage/signup/TermsOfServicePage.dart'; +import 'package:blueberry_flutter_template/widgets/signup/EmailDuplicateWidget.dart'; +import 'package:blueberry_flutter_template/widgets/signup/EmailVerifyWidget.dart'; +import 'package:blueberry_flutter_template/widgets/signup/NameInputWidget.dart'; +import 'package:blueberry_flutter_template/widgets/signup/NickNameInputWidget.dart'; +import 'package:blueberry_flutter_template/widgets/signup/PasswordConfirmWidget.dart'; +import 'package:blueberry_flutter_template/widgets/signup/PasswordInputWidget.dart'; +import 'package:blueberry_flutter_template/widgets/signup/PrivacyPolicyWidget.dart'; +import 'package:blueberry_flutter_template/widgets/signup/TermsOfServiceWidget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - import 'signup/ConfirmationPage.dart'; -import 'signup/EmailInputPage.dart'; -import 'signup/NameInputPage.dart'; -import 'signup/PrivacyPolicyPage.dart'; - -final PageController _pageController = PageController(); - -// 이메일 인증 ( 아이디 ) -final emailProvider = StateProvider((ref) => ''); -final emailVerificationCodeProvider = StateProvider((ref) => ''); -// 이름, 닉네임 생성 -final nameProvider = StateProvider((ref) => ''); -final nicknameProvider = StateProvider((ref) => ''); -// 비밀번호 생성 -final passwordProvider = StateProvider((ref) => ''); -final passwordConfirmProvider = StateProvider((ref) => ''); +final PageController _pageController = PageController(); -// 휴대폰 번호 인증 ( 구매할 때 휴대폰 인증 ) ( 따로 만들기 ) -final residentRegistrationNumberProvider = StateProvider((ref) => ''); -final phoneNumberProvider = StateProvider((ref) => ''); -final verificationNumberProvider = StateProvider((ref) => ''); class SignUpScreen extends StatefulWidget { const SignUpScreen({super.key}); @@ -44,31 +30,31 @@ class _SignUpScreenState extends State { controller: _pageController, physics: const NeverScrollableScrollPhysics(), children: [ - EmailInputScreen( + EmailInputPage( onNext: () => _pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ), ), - EmailVerificationPage( + PasswordInputPage( onNext: () => _pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ), ), - NameInputPage( + PasswordConfirmPage( onNext: () => _pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ), ), - PasswordInputPage( + NameNickNameInputPage( onNext: () => _pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, ), ), - PasswordConfirmPage( + EmailVerifyPage( onNext: () => _pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOut, @@ -94,3 +80,82 @@ class _SignUpScreenState extends State { ); } } + + +class EmailInputPage extends StatelessWidget { + final VoidCallback onNext; + const EmailInputPage({super.key, required this.onNext}); + + @override + Widget build(BuildContext context) { + return EmailDuplicateWidget(onNext: onNext); + } +} + +class PasswordInputPage extends StatelessWidget { + final VoidCallback onNext; + const PasswordInputPage({super.key, required this.onNext}); + + @override + Widget build(BuildContext context) { + return PasswordInputWidget(onNext: onNext); + } +} + +class PasswordConfirmPage extends StatelessWidget { + final VoidCallback onNext; + const PasswordConfirmPage({super.key, required this.onNext}); + + @override + Widget build(BuildContext context) { + return PasswordConfirmWidget(onNext: onNext); + } +} + +class NameNickNameInputPage extends StatelessWidget { + final VoidCallback onNext; + const NameNickNameInputPage({super.key, required this.onNext}); + + @override + Widget build(BuildContext context) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + NameInputWidget(), + NickNameInputWidget(onNext: onNext), + ], + ), + ); + } +} + +class EmailVerifyPage extends StatelessWidget { + final VoidCallback onNext; + const EmailVerifyPage({super.key, required this.onNext}); + + @override + Widget build(BuildContext context) { + return EmailVerifyWidget(onNext: onNext); + } +} + +class TermsOfServicePage extends StatelessWidget { + final VoidCallback onNext; + const TermsOfServicePage({super.key, required this.onNext}); + + @override + Widget build(BuildContext context) { + return TermsOfServiceWidget(onNext: onNext); + } +} + +class PrivacyPolicyPage extends StatelessWidget { + final VoidCallback onNext; + const PrivacyPolicyPage({super.key, required this.onNext}); + + @override + Widget build(BuildContext context) { + return PrivacyPolicyWidget(onNext: onNext); + } +} diff --git a/lib/screens/mypage/signup/ConfirmationPage.dart b/lib/screens/mypage/signup/ConfirmationPage.dart index f92d38d..efd647a 100644 --- a/lib/screens/mypage/signup/ConfirmationPage.dart +++ b/lib/screens/mypage/signup/ConfirmationPage.dart @@ -1,5 +1,8 @@ import 'dart:async'; +import 'package:blueberry_flutter_template/providers/SignUpDataProviders.dart'; +import 'package:blueberry_flutter_template/providers/user/FirebaseAuthServiceProvider.dart'; +import 'package:blueberry_flutter_template/screens/mypage/MyPageScreen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -19,8 +22,6 @@ class ConfirmationPage extends ConsumerWidget { const ConfirmationPage({super.key, required this.onNext}); - get firebaseAuthServiceProvider => null; - @override Widget build(BuildContext context, WidgetRef ref) { final email = ref.read(emailProvider.notifier); @@ -57,8 +58,9 @@ class ConfirmationPage extends ConsumerWidget { const SizedBox(height: 20), isLoading.when( data: (value) => ElevatedButton( - onPressed: - signUp(email.state, password.state, name.state, context, ref), + onPressed: () async { + await upDateUserDB(email.state, name.state, context, ref); + }, child: const Text('가입하기'), ), loading: () => const CircularProgressIndicator(), @@ -69,35 +71,25 @@ class ConfirmationPage extends ConsumerWidget { ); } - signUp(email, password, name, context, ref) async { + Future upDateUserDB(email, name, BuildContext context, WidgetRef ref) async { try { - // 사용자 계정 생성 - var userCredential = await ref - .read(firebaseAuthServiceProvider) - .signUpWithEmailPassword(email, password); + var currentUser = await ref.read(firebaseAuthServiceProvider).getCurrentUser()!; - // 새로운 UserDTO 인스턴스 생성 UserModel newUser = UserModel( - userId: userCredential!.uid, - email: email, + userId: currentUser.uid, name: name, - // 초기 이름 값, 필요에 따라 수정 - age: 0, - // 초기 나이 값, 필요에 따라 수정 + email: email, + age: 1, profileImageUrl: '', - // 초기 프로필 사진 URL, 필요에 따라 수정 - createdAt: DateTime.now() // 계정 생성 날짜 - ); + createdAt: DateTime.now()); - // Firestore에 사용자 정보 저장 await ref.read(firebaseStoreServiceProvider).createUser(newUser); - Navigator.pop(context); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text(AppStrings.signUpSuccessMessage)), ); + Navigator.push(context, MaterialPageRoute(builder: (context) => MyPageScreen())); } catch (e) { - // 회원가입 실패 시, 에러 메시지 출력 print('회원가입 실패: $e'); ScaffoldMessenger.of(context).showSnackBar(SnackBar( content: Text('회원가입 실패: $e'), diff --git a/lib/screens/mypage/signup/EmailInputPage.dart b/lib/screens/mypage/signup/EmailInputPage.dart deleted file mode 100644 index 311297c..0000000 --- a/lib/screens/mypage/signup/EmailInputPage.dart +++ /dev/null @@ -1,155 +0,0 @@ - - -import 'package:blueberry_flutter_template/providers/user/SignUpEmailDuplicationProvider.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../SignUpScreen.dart'; - -// 이메일 인증 로직을 담당하는 Provider -final emailVerificationProvider = - StateNotifierProvider( - (ref) { - return EmailVerificationNotifier(); -}); - -class EmailVerificationNotifier extends StateNotifier { - EmailVerificationNotifier() : super(EmailVerificationInitial()); - - Future sendVerificationCode(String email) async { - try { - // 이메일 인증 코드 발송 API 호출 - await sendEmailVerificationCode(email); - state = EmailVerificationCodeSent(); - } catch (e) { - state = EmailVerificationError(e.toString()); - } - } - - Future verifyCode(String email, String code) async { - try { - // 이메일 인증 코드 검증 API 호출 - await verifyEmailCode(email, code); - state = EmailVerificationSuccess(); - } catch (e) { - state = EmailVerificationError(e.toString()); - } - } -} - -// 이메일 인증 상태를 관리하는 클래스들 -abstract class EmailVerificationState {} - -class EmailVerificationInitial extends EmailVerificationState {} - -class EmailVerificationCodeSent extends EmailVerificationState {} - -class EmailVerificationSuccess extends EmailVerificationState {} - -class EmailVerificationError extends EmailVerificationState { - final String message; - - EmailVerificationError(this.message); -} - -class EmailInputPage extends ConsumerWidget { - final VoidCallback onNext; - final TextEditingController _emailController = TextEditingController(); - - EmailInputPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final email = ref.read(emailProvider.notifier); - final emailVerification = ref.read(emailVerificationProvider.notifier); - final emailDuplicate = ref.watch(emailDuplicateProvider.notifier); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextField( - controller: _emailController, - onChanged: (value) => email.state = value, - decoration: const InputDecoration(labelText: '이메일 입력'), - ), - const SizedBox(height: 20), - ElevatedButton(onPressed: () async { - String email = _emailController.text; - bool isDuplicate = await emailDuplicate.isDuplication(email); - if (isDuplicate) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('중복된 이메일입니다.')), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('사용 가능한 이메일입니다.')), - ); - } - }, child: const Text('중복 확인') - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () async { - await emailVerification.sendVerificationCode(email.state); - onNext(); - }, - child: const Text('인증번호 전송'), - ), - ], - ), - ); - } -} - -class EmailVerificationPage extends ConsumerWidget { - final VoidCallback onNext; - - const EmailVerificationPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final email = ref.read(emailProvider.notifier); - final emailVerification = ref.read(emailVerificationProvider.notifier); - final verificationCode = ref.watch(emailVerificationCodeProvider.notifier); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextField( - onChanged: (value) => verificationCode.state = value, - decoration: const InputDecoration(labelText: '이메일 코드 입력'), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: () { - try { - verifyEmailCode(email.state, verificationCode.state); - onNext(); - } catch (e) { - print(e); - } - }, - child: const Text('Next'), - ), - ], - ), - ); - } -} - -// 예시로 API 호출 메서드를 더미로 구현 -Future sendEmailVerificationCode(String email) async { - await Future.delayed(const Duration(seconds: 2)); - // 실제 API 호출 로직이 필요함 -} - -Future verifyEmailCode(String email, String code) async { - await Future.delayed(const Duration(seconds: 2)); - print("code: $code"); - // 실제 API 호출 로직이 필요함 - if (code != "123456") throw Exception("Invalid code"); -} diff --git a/lib/screens/mypage/signup/EmailInputScreen.dart b/lib/screens/mypage/signup/EmailInputScreen.dart deleted file mode 100644 index f46d960..0000000 --- a/lib/screens/mypage/signup/EmailInputScreen.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'package:blueberry_flutter_template/widgets/signup/EmailDuplicateWidget.dart'; -import 'package:flutter/material.dart'; - -class EmailInputScreen extends StatelessWidget { - final VoidCallback onNext; - const EmailInputScreen({super.key, required this.onNext}); - - @override - Widget build(BuildContext context) { - return EmailDuplicateWidget(onNext: onNext); - } -} diff --git a/lib/screens/mypage/signup/PasswordInputPage.dart b/lib/screens/mypage/signup/PasswordInputPage.dart deleted file mode 100644 index 1bf6428..0000000 --- a/lib/screens/mypage/signup/PasswordInputPage.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../SignUpScreen.dart'; - -class PasswordInputPage extends ConsumerWidget { - final VoidCallback onNext; - - const PasswordInputPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final password = ref.watch(passwordProvider.notifier); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextField( - onChanged: (value) => password.state = value, - decoration: const InputDecoration(labelText: '비밀번호 입력'), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: onNext, - child: const Text('Next'), - ), - ], - ), - ); - } -} - -class PasswordConfirmPage extends ConsumerWidget { - final VoidCallback onNext; - - const PasswordConfirmPage({super.key, required this.onNext}); - - @override - Widget build(BuildContext context, WidgetRef ref) { - final password = ref.watch(passwordProvider.notifier); - final passwordConfirm = ref.watch(passwordConfirmProvider.notifier); - - return Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextField( - onChanged: (value) => passwordConfirm.state = value, - decoration: const InputDecoration(labelText: '비밀번호 확인'), - ), - const SizedBox(height: 20), - ElevatedButton( - onPressed: onNext, - child: const Text('Next'), - ), - ], - ), - ); - } -} diff --git a/lib/utils/AppStrings.dart b/lib/utils/AppStrings.dart index 043471f..40cd78b 100644 --- a/lib/utils/AppStrings.dart +++ b/lib/utils/AppStrings.dart @@ -60,9 +60,15 @@ class AppStrings { '비밀번호는 최소 8자 이상이어야 하며, 하나 이상의 문자와 숫자가 포함되어야 합니다.'; static const String errorMessage_emptyEmail = '이메일 주소를 입력하세요.'; static const String errorMessage_invalidEmail = '유효한 이메일 주소를 입력하세요.'; + static const String errorMessage_checkEmail = '이메일 주소를 확인해주세요.'; static const String errorMessage_emailAlreadyInUse = '이미 다른 계정에서 사용 중인 이메일 주소입니다.'; static const String errorMessage_weakPassword = '비밀번호가 너무 약합니다.'; static const String errorMessage_userNotFound = '이 식별자에 해당하는 사용자 기록이 없습니다. 사용자가 삭제되었을 수 있습니다.'; + static const String errorMessage_wrongPassword = '비밀번호가 잘못 되었습니다. 다시 시도 해 주세요.'; + static const String errorMessage_regexpPassword = '비밀번호는 최소 8자 이상이어야 하며, 두개 이상의 문자와 숫자가 포함되어야 합니다.'; + static const String errorMessage_checkPassword = '비밀번호를 확인 해주세요.'; + static const String errorMessage_duplicatedPassword = '비밀번호가 일치하지 않습니다.'; + // Company Info static const String companyInfoTitle = '회사 정보'; @@ -80,4 +86,13 @@ class AppStrings { static const String errorMessage_emptyVerificationCode = '인증번호를 입력해주세요.'; static const String errorMessage_invalidVerificationCode = '인증번호를 확인해주세요.'; + //Email Verification + static const String send_emailVerification = '이메일 인증 메일을 보냈습니다. 확인해주세요.'; + static const String check_emailVerification = '이메일 인증을 확인해주세요.'; + static const String click_emailVerification = '인증을 완료 하셨다면 버튼을 눌러주세요'; + + //Success Handling + static const String successMessage_emailValidation = '사용 가능한 이메일입니다'; + + } diff --git a/lib/widgets/signup/EmailDuplicateWidget.dart b/lib/widgets/signup/EmailDuplicateWidget.dart index 7d43589..808cff0 100644 --- a/lib/widgets/signup/EmailDuplicateWidget.dart +++ b/lib/widgets/signup/EmailDuplicateWidget.dart @@ -1,6 +1,7 @@ +import 'package:blueberry_flutter_template/providers/SignUpDataProviders.dart'; import 'package:blueberry_flutter_template/providers/user/SignUpEmailDuplicationProvider.dart'; import 'package:blueberry_flutter_template/screens/mypage/SignUpScreen.dart'; -import 'package:blueberry_flutter_template/screens/mypage/signup/EmailInputPage.dart'; +import 'package:blueberry_flutter_template/utils/AppStrings.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter/material.dart'; @@ -20,64 +21,80 @@ class _EmailDuplicateWidgetState extends ConsumerState { @override Widget build(BuildContext context) { final email = ref.read(emailProvider.notifier); - final emailVerification = ref.read(emailVerificationProvider.notifier); final emailDuplicate = ref.watch(emailDuplicateProvider.notifier); + final formKey = GlobalKey(); + final RegExp emailRegExp = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'); return Padding( padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextField( - controller: _emailController, - onChanged: (value) => email.state = value, - decoration: const InputDecoration(labelText: '이메일 입력'), - ), - const SizedBox(height: 20), + child: Form( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextFormField( + controller: _emailController, + onChanged: (value) => email.state = value, + decoration: const InputDecoration(labelText: AppStrings.emailInputLabel), + validator: (value) { + if (value == null || value.isEmpty) { + return AppStrings.errorMessage_emptyEmail; + } else if (!emailRegExp.hasMatch(value)) { + return AppStrings.errorMessage_invalidEmail; + } + return null; + }, + ), + const SizedBox(height: 20), - _duplicationBtn(emailDuplicate, context), + _duplicationBtn(emailDuplicate, context, formKey), - const SizedBox(height: 20), + const SizedBox(height: 20), - _verifyBtn(emailVerification, email), - ], + _nextBtn(formKey, context) + ], + ), ), ); } - ElevatedButton _verifyBtn(EmailVerificationNotifier emailVerification, StateController email) { + ElevatedButton _duplicationBtn(EmailDuplicateNotifier emailDuplicate, BuildContext context, GlobalKey formKey) { return ElevatedButton( onPressed: () async { - await emailVerification.sendVerificationCode(email.state); - widget.onNext(); - }, - child: const Text('인증번호 전송'), - ); - } - - ElevatedButton _duplicationBtn(EmailDuplicateNotifier emailDuplicate, BuildContext context) { - return ElevatedButton( - onPressed: () async { - String email = _emailController.text; - bool isDuplicate = await emailDuplicate.isDuplication(email); - if (isDuplicate) { - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('중복된 이메일입니다.')), - ); - setState(() { - isEmailAvailable = false; - }); + if (formKey.currentState?.validate() ?? false) { + String email = _emailController.text; + bool isDuplicate = await emailDuplicate.isDuplication(email); + if (isDuplicate) { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text(AppStrings.errorMessage_emailAlreadyInUse)), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text(AppStrings.successMessage_emailValidation)), + ); + } } else { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('사용 가능한 이메일입니다.')), + const SnackBar(content: Text(AppStrings.errorMessage_checkEmail)), ); - setState(() { - isEmailAvailable = true; - }); } }, child: const Text('중복 확인'), ); } + + ElevatedButton _nextBtn(GlobalKey formKey, BuildContext context) { + return ElevatedButton(onPressed: (){ + if (formKey.currentState?.validate() ?? false) { + widget.onNext(); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text(AppStrings.errorMessage_checkEmail)), + ); + } + }, child: const Text('NEXT')); + } } + + diff --git a/lib/widgets/signup/EmailVerifyWidget.dart b/lib/widgets/signup/EmailVerifyWidget.dart new file mode 100644 index 0000000..cad54ba --- /dev/null +++ b/lib/widgets/signup/EmailVerifyWidget.dart @@ -0,0 +1,43 @@ +import 'package:blueberry_flutter_template/providers/user/FirebaseAuthServiceProvider.dart'; +import 'package:blueberry_flutter_template/screens/mypage/MyPageScreen.dart'; +import 'package:blueberry_flutter_template/utils/AppStrings.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class EmailVerifyWidget extends ConsumerWidget { + final VoidCallback onNext; + const EmailVerifyWidget({super.key, required this.onNext}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(AppStrings.send_emailVerification), + Text(AppStrings.check_emailVerification), + ElevatedButton( + onPressed: () async { + try{ + final verifierUser = ref.watch(firebaseAuthServiceProvider).getCurrentUser(); + if (verifierUser != null) { + await verifierUser.reload(); + final reloadedUser = ref.watch(firebaseAuthServiceProvider).getCurrentUser(); + if (reloadedUser?.emailVerified == true) { + onNext(); + } else { + await reloadedUser?.sendEmailVerification(); + } + } + } catch(e) { + print(e); + } + + }, + child: Text(AppStrings.click_emailVerification) + ) + ], + ), + ); + } +} diff --git a/lib/widgets/signup/NameInputWidget.dart b/lib/widgets/signup/NameInputWidget.dart new file mode 100644 index 0000000..7198a6b --- /dev/null +++ b/lib/widgets/signup/NameInputWidget.dart @@ -0,0 +1,34 @@ +import 'package:blueberry_flutter_template/providers/SignUpDataProviders.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + + + +class NameInputWidget extends ConsumerWidget { + + const NameInputWidget({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final name = ref.watch(nameProvider.notifier); + final formKey = GlobalKey(); + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextFormField( + onChanged: (value) => name.state = value, + decoration: const InputDecoration(labelText: '이름 입력'), + // 특문 막는 정규식 써야함 + ), + const SizedBox(height: 20), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/mypage/signup/NameInputPage.dart b/lib/widgets/signup/NickNameInputWidget.dart similarity index 51% rename from lib/screens/mypage/signup/NameInputPage.dart rename to lib/widgets/signup/NickNameInputWidget.dart index 92a6cbd..1ba0e4b 100644 --- a/lib/screens/mypage/signup/NameInputPage.dart +++ b/lib/widgets/signup/NickNameInputWidget.dart @@ -1,27 +1,16 @@ +import 'package:blueberry_flutter_template/providers/SignUpDataProviders.dart'; +import 'package:blueberry_flutter_template/providers/user/FirebaseAuthServiceProvider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import '../SignUpScreen.dart'; -class NameInputScreen extends StatelessWidget { +class NickNameInputWidget extends ConsumerWidget { final VoidCallback onNext; - const NameInputScreen({super.key, required this.onNext}); - @override - Widget build(BuildContext context) { - return NameInputScreen(onNext: onNext); - } -} - - -class NameInputPage extends ConsumerWidget { - final VoidCallback onNext; - - const NameInputPage({super.key, required this.onNext}); + const NickNameInputWidget({super.key, required this.onNext}); @override Widget build(BuildContext context, WidgetRef ref) { - final name = ref.watch(nameProvider.notifier); final nickname = ref.watch(nicknameProvider.notifier); return Padding( @@ -29,21 +18,25 @@ class NameInputPage extends ConsumerWidget { child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - TextField( - onChanged: (value) => name.state = value, - decoration: const InputDecoration(labelText: '이름 입력'), - ), - const SizedBox(height: 20), TextField( onChanged: (value) => nickname.state = value, decoration: const InputDecoration(labelText: '닉네임 입력'), + // 특문 막는 정규식, 일부 금칙어 설정 해야함 ), ElevatedButton( - onPressed: onNext, + onPressed: () { + try { + final user = ref.watch(firebaseAuthServiceProvider).getCurrentUser(); + user?.sendEmailVerification(); + onNext(); + } catch (e) { + print(e); + } + }, child: const Text('Next'), ), ], ), ); } -} +} \ No newline at end of file diff --git a/lib/widgets/signup/PasswordConfirmWidget.dart b/lib/widgets/signup/PasswordConfirmWidget.dart new file mode 100644 index 0000000..e00647f --- /dev/null +++ b/lib/widgets/signup/PasswordConfirmWidget.dart @@ -0,0 +1,57 @@ +import 'package:blueberry_flutter_template/providers/SignUpDataProviders.dart'; +import 'package:blueberry_flutter_template/providers/user/FirebaseAuthServiceProvider.dart'; +import 'package:blueberry_flutter_template/screens/mypage/SignUpScreen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../utils/AppStrings.dart'; + +class PasswordConfirmWidget extends ConsumerWidget { + final VoidCallback onNext; + + const PasswordConfirmWidget({super.key, required this.onNext}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final email = ref.read(emailProvider); + final password = ref.read(passwordProvider); + final passwordConfirm = ref.watch(passwordConfirmProvider.notifier); + final formKey = GlobalKey(); + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextFormField( + onChanged: (value) => passwordConfirm.state = value, + decoration: const InputDecoration(labelText: '비밀번호 확인'), + validator: (value) { + if (value == null || value.isEmpty) { + return AppStrings.errorMessage_emptyPassword; + } else if (password != value) { + return AppStrings.errorMessage_duplicatedPassword; + } + return null; + }, + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () async { + try { + await ref.watch(firebaseAuthServiceProvider).signUpWithEmailPassword(email, passwordConfirm.state); + // 오류 뱉어내는거 하나 만들어야함 ex ) ID or Password 형식에 문제가 있다라고 쏴야할듯 ? + onNext(); + } catch(e) { + print('failed signUp $e'); + } + }, + child: const Text('Next'), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/widgets/signup/PasswordInputWidget.dart b/lib/widgets/signup/PasswordInputWidget.dart new file mode 100644 index 0000000..8dbc447 --- /dev/null +++ b/lib/widgets/signup/PasswordInputWidget.dart @@ -0,0 +1,59 @@ +import 'package:blueberry_flutter_template/providers/SignUpDataProviders.dart'; +import 'package:blueberry_flutter_template/providers/user/FirebaseAuthServiceProvider.dart'; +import 'package:blueberry_flutter_template/screens/mypage/SignUpScreen.dart'; +import 'package:blueberry_flutter_template/utils/AppStrings.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + + +class PasswordInputWidget extends ConsumerWidget { + final VoidCallback onNext; + + const PasswordInputWidget({super.key, required this.onNext}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final password = ref.watch(passwordProvider.notifier); + final RegExp passwordRegExp = RegExp(r'^(?=.*[!@#\$&*~])(?=.*[!@#\$&*~]).{8,}$'); + final formKey = GlobalKey(); + + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Form( + key: formKey, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextFormField( + onChanged: (value) => password.state = value, + decoration: const InputDecoration(labelText: '비밀번호 입력'), + obscureText: true, + validator: (value) { + if (value == null || value.isEmpty) { + return AppStrings.errorMessage_emptyPassword; + } else if (!passwordRegExp.hasMatch(value)) { + return AppStrings.errorMessage_regexpPassword; + } + return null; + }, + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: (){ + if (formKey.currentState?.validate() ?? false) { + onNext(); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text(AppStrings.errorMessage_checkPassword)), + ); + } + }, + child: const Text('Next'), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/screens/mypage/signup/PrivacyPolicyPage.dart b/lib/widgets/signup/PrivacyPolicyWidget.dart similarity index 96% rename from lib/screens/mypage/signup/PrivacyPolicyPage.dart rename to lib/widgets/signup/PrivacyPolicyWidget.dart index a49290f..9d8d9bc 100644 --- a/lib/screens/mypage/signup/PrivacyPolicyPage.dart +++ b/lib/widgets/signup/PrivacyPolicyWidget.dart @@ -1,22 +1,15 @@ import 'package:flutter/material.dart'; -/// PrivacyPolicyPage.dart -/// -/// Privacy Policy Page -/// - 개인정보 처리방침 페이지 -/// -/// @jwson-automation - -class PrivacyPolicyPage extends StatefulWidget { +class PrivacyPolicyWidget extends StatefulWidget { final VoidCallback onNext; - const PrivacyPolicyPage({super.key, required this.onNext}); + const PrivacyPolicyWidget({super.key, required this.onNext}); @override - State createState() => _PrivacyPolicyPageState(); + State createState() => _PrivacyPolicyPageState(); } -class _PrivacyPolicyPageState extends State { +class _PrivacyPolicyPageState extends State { final ScrollController _scrollController = ScrollController(); bool isBottom = false; @@ -111,4 +104,4 @@ o 개인정보보호책임자 성명 : $name : 개인 전화번호 : $phone 이 o 귀하께서는 회사의 서비스를 이용하시며 발생하는 모든 개인정보보호 관련 민원을 개인정보보호책임자 혹은 담당부서로 신고하실 수 있습니다. o 회사는 이용자들의 신고사항에 대해 신속하게 충분한 답변을 드릴 것입니다. o 기타 개인정보침해에 대한 신고나 상담이 필요하신 경우에는 아래 기관에 문의하시기 바랍니다. 개인정보침해신고센터 (privacy.kisa.or.kr / 국번 없이 118) 개인정보분쟁조정위원회 (kopico.go.kr / 1833-6972) 대검찰청 사이버수사과 (spo.go.kr / 지역번호+1301) 경찰청 사이버안전국 (cyberbureau.police.go.kr / 국번없이 182) '''; -} +} \ No newline at end of file diff --git a/lib/screens/mypage/signup/TermsOfServicePage.dart b/lib/widgets/signup/TermsOfServiceWidget.dart similarity index 98% rename from lib/screens/mypage/signup/TermsOfServicePage.dart rename to lib/widgets/signup/TermsOfServiceWidget.dart index 7986829..7872296 100644 --- a/lib/screens/mypage/signup/TermsOfServicePage.dart +++ b/lib/widgets/signup/TermsOfServiceWidget.dart @@ -1,22 +1,15 @@ import 'package:flutter/material.dart'; -/// TermsOfServicePage.dart -/// -/// Terms of Service Page -/// - 쇼핑몰 개인정보처리방침 페이지 -/// -/// @jwson-automation - -class TermsOfServicePage extends StatefulWidget { +class TermsOfServiceWidget extends StatefulWidget { final VoidCallback onNext; - const TermsOfServicePage({super.key, required this.onNext}); + const TermsOfServiceWidget({super.key, required this.onNext}); @override - State createState() => _TermsOfServicePageState(); + State createState() => _TermsOfServicePageState(); } -class _TermsOfServicePageState extends State { +class _TermsOfServicePageState extends State { final ScrollController _scrollController = ScrollController(); bool isBottom = false; @@ -53,10 +46,10 @@ class _TermsOfServicePageState extends State { isBottom ? widget.onNext() : _scrollController.animateTo( - _scrollController.position.maxScrollExtent, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); }, child: const Icon(Icons.check), ))); @@ -145,4 +138,4 @@ Column getTerms() { '제24조(재판권 및 준거법)\n ① “몰”과 이용자 간에 발생한 전자상거래 분쟁에 관한 소송은 제소 당시의 이용자의 주소에 의하고, 주소가 없는 경우에는 거소를 관할하는 지방법원의 전속관할로 합니다. 다만, 제소 당시 이용자의 주소 또는 거소가 분명하지 않거나 외국 거주자의 경우에는 민사소송법상의 관할법원에 제기합니다.\n ② “몰”과 이용자 간에 제기된 전자상거래 소송에는 한국법을 적용합니다.'), ], ); -} +} \ No newline at end of file