diff --git a/.gitignore b/.gitignore index afa86de..70c3083 100644 --- a/.gitignore +++ b/.gitignore @@ -111,7 +111,7 @@ lib/generated_plugin_registrant.dart # Xcode # # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - +ios/Podfile.lock ## User settings xcuserdata/ diff --git a/assets/icons/bubble.svg b/assets/icons/bubble.svg new file mode 100644 index 0000000..cb7118f --- /dev/null +++ b/assets/icons/bubble.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Podfile.lock b/ios/Podfile.lock deleted file mode 100644 index 55d7bf5..0000000 --- a/ios/Podfile.lock +++ /dev/null @@ -1,124 +0,0 @@ -PODS: - - Firebase/Auth (11.10.0): - - Firebase/CoreOnly - - FirebaseAuth (~> 11.10.0) - - Firebase/CoreOnly (11.10.0): - - FirebaseCore (~> 11.10.0) - - firebase_auth (5.5.2): - - Firebase/Auth (= 11.10.0) - - firebase_core - - Flutter - - firebase_core (3.13.0): - - Firebase/CoreOnly (= 11.10.0) - - Flutter - - FirebaseAppCheckInterop (11.11.0) - - FirebaseAuth (11.10.0): - - FirebaseAppCheckInterop (~> 11.0) - - FirebaseAuthInterop (~> 11.0) - - FirebaseCore (~> 11.10.0) - - FirebaseCoreExtension (~> 11.10.0) - - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - - GoogleUtilities/Environment (~> 8.0) - - GTMSessionFetcher/Core (< 5.0, >= 3.4) - - RecaptchaInterop (~> 101.0) - - FirebaseAuthInterop (11.11.0) - - FirebaseCore (11.10.0): - - FirebaseCoreInternal (~> 11.10.0) - - GoogleUtilities/Environment (~> 8.0) - - GoogleUtilities/Logger (~> 8.0) - - FirebaseCoreExtension (11.10.0): - - FirebaseCore (~> 11.10.0) - - FirebaseCoreInternal (11.10.0): - - "GoogleUtilities/NSData+zlib (~> 8.0)" - - Flutter (1.0.0) - - GoogleUtilities/AppDelegateSwizzler (8.0.2): - - GoogleUtilities/Environment - - GoogleUtilities/Logger - - GoogleUtilities/Network - - GoogleUtilities/Privacy - - GoogleUtilities/Environment (8.0.2): - - GoogleUtilities/Privacy - - GoogleUtilities/Logger (8.0.2): - - GoogleUtilities/Environment - - GoogleUtilities/Privacy - - GoogleUtilities/Network (8.0.2): - - GoogleUtilities/Logger - - "GoogleUtilities/NSData+zlib" - - GoogleUtilities/Privacy - - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (8.0.2)": - - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (8.0.2) - - GoogleUtilities/Reachability (8.0.2): - - GoogleUtilities/Logger - - GoogleUtilities/Privacy - - GTMSessionFetcher/Core (4.4.0) - - kakao_flutter_sdk_common (1.9.7-3): - - Flutter - - RecaptchaInterop (101.0.0) - - screen_protector (1.2.1): - - Flutter - - ScreenProtectorKit (~> 1.3.1) - - ScreenProtectorKit (1.3.1) - - shared_preferences_foundation (0.0.1): - - Flutter - - FlutterMacOS - -DEPENDENCIES: - - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - - Flutter (from `Flutter`) - - kakao_flutter_sdk_common (from `.symlinks/plugins/kakao_flutter_sdk_common/ios`) - - screen_protector (from `.symlinks/plugins/screen_protector/ios`) - - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - -SPEC REPOS: - trunk: - - Firebase - - FirebaseAppCheckInterop - - FirebaseAuth - - FirebaseAuthInterop - - FirebaseCore - - FirebaseCoreExtension - - FirebaseCoreInternal - - GoogleUtilities - - GTMSessionFetcher - - RecaptchaInterop - - ScreenProtectorKit - -EXTERNAL SOURCES: - firebase_auth: - :path: ".symlinks/plugins/firebase_auth/ios" - firebase_core: - :path: ".symlinks/plugins/firebase_core/ios" - Flutter: - :path: Flutter - kakao_flutter_sdk_common: - :path: ".symlinks/plugins/kakao_flutter_sdk_common/ios" - screen_protector: - :path: ".symlinks/plugins/screen_protector/ios" - shared_preferences_foundation: - :path: ".symlinks/plugins/shared_preferences_foundation/darwin" - -SPEC CHECKSUMS: - Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2 - firebase_auth: e37065f3f80ff90580c13ad0e5a48e3bb8d2ad77 - firebase_core: 432718558359a8c08762151b5f49bb0f093eb6e0 - FirebaseAppCheckInterop: f23709c9ce92d810aa53ff4ce12ad3e666a3c7be - FirebaseAuth: c4146bdfdc87329f9962babd24dae89373f49a32 - FirebaseAuthInterop: ac22ed402c2f4e3a8c63ebd3278af9a06073c1be - FirebaseCore: 8344daef5e2661eb004b177488d6f9f0f24251b7 - FirebaseCoreExtension: 6f357679327f3614e995dc7cf3f2d600bdc774ac - FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679 - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d - GTMSessionFetcher: 75b671f9e551e4c49153d4c4f8659ef4f559b970 - kakao_flutter_sdk_common: 3dc8492c202af7853585d151490b1c5c6b7576cb - RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba - screen_protector: 6f92086bd2f2f4b54f54913289b9d1310610140b - ScreenProtectorKit: 83a6281b02c7a5902ee6eac4f5045f674e902ae4 - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - -PODFILE CHECKSUM: f8c2dcdfb50bb67645580d28a6bf814fca30bdec - -COCOAPODS: 1.16.2 diff --git a/lib/auth/presentation/pages/congratulation/congratulation_page.dart b/lib/auth/presentation/pages/congratulation/congratulation_page.dart new file mode 100644 index 0000000..a3d7180 --- /dev/null +++ b/lib/auth/presentation/pages/congratulation/congratulation_page.dart @@ -0,0 +1,117 @@ +import 'package:code_l/auth/presentation/pages/congratulation/widgets/congratulation_app_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import '../../../../core/utills/design/app_colors.dart'; +import '../../../../core/utills/design/app_gaps.dart'; +import '../../../../core/utills/design/app_typography.dart'; +import 'widgets/congratulation_confirm_button.dart'; + +class CongratulationPage extends StatelessWidget { + const CongratulationPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: const CongratulationAppBar(), + body: _buildContentField(), + ); + } + + Widget _buildContentField() { + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: AppGaps.gap20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 30), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SvgPicture.asset("assets/icons/logo_with_image.svg"), + SizedBox(height: AppGaps.gap12), + Text( + "가입을 축하합니다!", + style: AppTypography.header2.copyWith( + color: AppColors.grey900, + ), + ), + ], + ), + ], + ), + SizedBox(height: 54), + Row( + children: [Text("개성있는 익명 활동 🤫", style: AppTypography.subtitle3)], + ), + Flexible( + child: Text( + "자신의 얼굴을 바로 공개하지 않고도 자유롭게 소통할 수 있어요.", + style: AppTypography.body2, + ), + ), + SizedBox(height: AppGaps.gap20), + Row( + children: [ + Text("안심할 수 있는 공개 시스템 🔐", style: AppTypography.subtitle3), + ], + ), + Flexible( + child: Text( + "원치 않는 사람에게 얼굴이 공개될 걱정 없이, 내가 선택한 사람에게만 보여줄 수 있어요.", + style: AppTypography.body2, + ), + ), + SizedBox(height: 54), + Container( + height: 114, + width: double.infinity, + alignment: Alignment.center, + color: AppColors.grey100, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: AppGaps.gap16), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("코드 프로필이란?", style: AppTypography.caption1), + SizedBox(height: AppGaps.gap8), + Flexible( + child: Text( + "이름 대신 코드 네임과 개성있는 정보를 통해 나를 표현하는 Code : L의 특별한 프로필 입니다. 관심사, 취향, 스타일 등 나만의 프로필로 나를 보여주세요!", + style: AppTypography.caption1.copyWith( + color: AppColors.grey700, + ), + ), + ), + ], + ), + ), + ), + SizedBox(height: 120), + Padding( + padding: const EdgeInsets.only(left: 18.0), + child: SvgPicture.asset( + "assets/icons/bubble.svg", + width: double.infinity, + height: 40, + ), + ), + + Padding( + padding: const EdgeInsets.all(AppGaps.gap4), + child: CongratulationConfirmButton( + enabled: true, + text: "코드 프로필 작성하기", + onPressed: () {}, + ), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/auth/presentation/pages/congratulation/widgets/congratulation_app_bar.dart b/lib/auth/presentation/pages/congratulation/widgets/congratulation_app_bar.dart new file mode 100644 index 0000000..dab9184 --- /dev/null +++ b/lib/auth/presentation/pages/congratulation/widgets/congratulation_app_bar.dart @@ -0,0 +1,40 @@ +import 'package:code_l/core/utills/design/app_gaps.dart'; +import 'package:flutter/material.dart'; + +import '../../../../../core/utills/design/app_colors.dart'; +import '../../../../../core/utills/design/app_typography.dart'; + +class CongratulationAppBar extends StatelessWidget + implements PreferredSizeWidget { + const CongratulationAppBar({super.key}); + + @override + Size get preferredSize => Size.fromHeight(56); + @override + Widget build(BuildContext context) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: AppGaps.gap36, + ), + Text( + "회원가입", + style: AppTypography.subtitle2.copyWith(color: AppColors.grey900), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/auth/presentation/pages/congratulation/widgets/congratulation_confirm_button.dart b/lib/auth/presentation/pages/congratulation/widgets/congratulation_confirm_button.dart new file mode 100644 index 0000000..1e95f38 --- /dev/null +++ b/lib/auth/presentation/pages/congratulation/widgets/congratulation_confirm_button.dart @@ -0,0 +1,40 @@ +import 'package:flutter/cupertino.dart'; + +import '../../../../../core/utills/design/app_colors.dart'; +import '../../../../../core/utills/design/app_typography.dart'; + +class CongratulationConfirmButton extends StatelessWidget { + const CongratulationConfirmButton({ + super.key, + required this.enabled, + this.onPressed, + this.text = "확인", + }); + + final bool enabled; + final VoidCallback? onPressed; + final String text; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: enabled ? onPressed : null, + child: Container( + height: 54, + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 16), + alignment: Alignment.center, + decoration: BoxDecoration( + color: enabled ? AppColors.primary : AppColors.grey200, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + text, + style: AppTypography.subtitle2.copyWith( + color: enabled ? AppColors.white : AppColors.grey400, + ), + ), + ), + ); + } +} diff --git a/lib/auth/presentation/pages/login/login_viewmodel.dart b/lib/auth/presentation/pages/login/login_viewmodel.dart index 830484f..68b315d 100644 --- a/lib/auth/presentation/pages/login/login_viewmodel.dart +++ b/lib/auth/presentation/pages/login/login_viewmodel.dart @@ -15,10 +15,9 @@ class LoginViewModel extends ChangeNotifier { try { bool installed = await isKakaoTalkInstalled(); - OAuthToken token = - installed - ? await UserApi.instance.loginWithKakaoTalk() - : await UserApi.instance.loginWithKakaoAccount(); + OAuthToken token = installed + ? await UserApi.instance.loginWithKakaoTalk() + : await UserApi.instance.loginWithKakaoAccount(); final user = await UserApi.instance.me(); diff --git a/lib/auth/presentation/pages/terms_and_conditions/providers.dart b/lib/auth/presentation/pages/terms_and_conditions/providers.dart new file mode 100644 index 0000000..578c774 --- /dev/null +++ b/lib/auth/presentation/pages/terms_and_conditions/providers.dart @@ -0,0 +1,5 @@ +import 'package:code_l/auth/presentation/pages/terms_and_conditions/terms_and_conditions_viewmodel.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final termsAndConditionsViewModelProvider = + ChangeNotifierProvider((ref) => TermsAndConditionsViewmodel()); diff --git a/lib/auth/presentation/pages/terms_and_conditions/terms_and_condition_page.dart b/lib/auth/presentation/pages/terms_and_conditions/terms_and_condition_page.dart new file mode 100644 index 0000000..704acc9 --- /dev/null +++ b/lib/auth/presentation/pages/terms_and_conditions/terms_and_condition_page.dart @@ -0,0 +1,132 @@ +import 'package:code_l/auth/presentation/pages/terms_and_conditions/providers.dart'; +import 'package:code_l/auth/presentation/pages/woman/woman_verification_Page.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../../core/utills/design/app_colors.dart'; +import '../../../../core/utills/design/app_gaps.dart'; +import '../../../../core/utills/design/app_typography.dart'; +import '../../../../sign_up/presentation/widgets/sign_up_confirm_button.dart'; +import '../../widgets/auth_confirm_button.dart'; +import 'widgets/terms_and_conditions_app_bar.dart'; +import '../login/login_page.dart'; + +class TermsAndConditionPage extends ConsumerWidget { + const TermsAndConditionPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final viewmodel = ref.watch(termsAndConditionsViewModelProvider); + final notifier = ref.read(termsAndConditionsViewModelProvider.notifier); + String getLabel(int index) { + switch (index) { + case 1: + return "서비스 이용 약관 동의 (필수)"; + case 2: + return "개인정보 수집 및 이용 동의 (필수)"; + case 3: + return "민감정보 수집 및 이용 동의 (필수)"; + case 4: + return "마케팅 정보 수신 동의 (선택)"; + default: + return ""; + } + } + + return Scaffold( + appBar: const AgreeToTermsAndConditionsAppBar(), + bottomNavigationBar: Padding( + padding: const EdgeInsets.all(AppGaps.gap20), + child: AuthConfirmButton( + enabled: viewmodel.isValid, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const WomanVerificationPage()), + ); + }, + ), + ), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: AppGaps.gap24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 20), + Text( + "서비스 이용에 필요한 약관 및 정책에\n동의해주세요", + style: AppTypography.subtitle1.copyWith( + color: AppColors.grey900, + ), + ), + const SizedBox(height: 60), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // "모두 동의합니다." Row + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "모두 동의합니다.", + style: AppTypography.subtitle2.copyWith( + color: viewmodel.checkState[0] + ? AppColors.grey900 + : AppColors.grey500, + ), + ), + IconButton( + onPressed: () => notifier.toggleAll(), + iconSize: 36, + icon: Icon( + Icons.check_circle_outline, + color: viewmodel.checkState[0] + ? AppColors.grey900 + : AppColors.grey500, + ), + ), + ], + ), + // Individual agreement rows + for (int i = 1; i < viewmodel.checkState.length; i++) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + TextButton( + style: TextButton.styleFrom( + padding: EdgeInsets.zero, + ), + onPressed: () {}, + child: Text( + getLabel(i), + style: AppTypography.body2.copyWith( + decoration: TextDecoration.underline, + color: viewmodel.checkState[i] + ? AppColors.grey900 + : AppColors.grey500, + ), + ), + ), + IconButton( + onPressed: () => notifier.toggle(i), + iconSize: 24, + icon: Icon( + Icons.check, + color: viewmodel.checkState[i] + ? AppColors.grey900 + : AppColors.grey500, + ), + ), + ], + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/auth/presentation/pages/terms_and_conditions/terms_and_conditions_viewmodel.dart b/lib/auth/presentation/pages/terms_and_conditions/terms_and_conditions_viewmodel.dart new file mode 100644 index 0000000..4c6e039 --- /dev/null +++ b/lib/auth/presentation/pages/terms_and_conditions/terms_and_conditions_viewmodel.dart @@ -0,0 +1,32 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class TermsAndConditionsViewmodel extends ChangeNotifier { + List checkState = [false, false, false, false, false]; + bool isValid = false; + void toggle(int index) { + checkState[index] = !checkState[index]; + + if (index > 0) { + final allChecked = checkState.sublist(1).every((e) => e); + checkState[0] = allChecked; + } + if (checkState.sublist(1, 4).every((checked) => checked)) { + isValid = true; + } else { + isValid = false; + } + notifyListeners(); + } + + void toggleAll() { + final allAgree = !checkState[0]; + checkState = [allAgree, for (int i = 1; i < checkState.length; i++) allAgree]; + if (checkState.sublist(1, 4).every((checked) => checked)) { + isValid = true; + } else { + isValid = false; + } + notifyListeners(); + } +} diff --git a/lib/auth/presentation/pages/terms_and_conditions/widgets/terms_and_conditions_app_bar.dart b/lib/auth/presentation/pages/terms_and_conditions/widgets/terms_and_conditions_app_bar.dart new file mode 100644 index 0000000..b16db4e --- /dev/null +++ b/lib/auth/presentation/pages/terms_and_conditions/widgets/terms_and_conditions_app_bar.dart @@ -0,0 +1,41 @@ +import 'package:code_l/core/utills/design/app_typography.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../../../../../core/utills/design/app_colors.dart'; + +class AgreeToTermsAndConditionsAppBar extends StatelessWidget + implements PreferredSizeWidget { + const AgreeToTermsAndConditionsAppBar({super.key}); + @override + Size get preferredSize => Size.fromHeight(56); + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + IconButton( + icon: const Icon(Icons.arrow_back_ios_new_rounded), + onPressed: () => Navigator.pop(context), + ), + Text( + "서비스 이용 약관 및 정책", + style: AppTypography.subtitle2.copyWith(color: AppColors.grey800), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/auth/presentation/pages/woman/providers.dart b/lib/auth/presentation/pages/woman/providers.dart new file mode 100644 index 0000000..1bfd11e --- /dev/null +++ b/lib/auth/presentation/pages/woman/providers.dart @@ -0,0 +1,5 @@ +import 'package:code_l/auth/presentation/pages/woman/woman_verification_viewmodel.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final womanViewmodelProvider = + ChangeNotifierProvider((ref) => WomanVerificationViewmodel()); diff --git a/lib/auth/presentation/pages/woman/widgets/woman_verification_app_bar.dart b/lib/auth/presentation/pages/woman/widgets/woman_verification_app_bar.dart new file mode 100644 index 0000000..2a96d2d --- /dev/null +++ b/lib/auth/presentation/pages/woman/widgets/woman_verification_app_bar.dart @@ -0,0 +1,40 @@ +import 'package:code_l/core/utills/design/app_gaps.dart'; +import 'package:flutter/material.dart'; + +import '../../../../../core/utills/design/app_colors.dart'; +import '../../../../../core/utills/design/app_typography.dart'; + +class WomanVerificationAppBar extends StatelessWidget + implements PreferredSizeWidget { + const WomanVerificationAppBar({super.key}); + + @override + Size get preferredSize => Size.fromHeight(56); + @override + Widget build(BuildContext context) { + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: AppGaps.gap36, + ), + Text( + "여성확인 안내", + style: AppTypography.subtitle2.copyWith(color: AppColors.grey900), + ), + IconButton( + icon: const Icon(Icons.close), + onPressed: () { + Navigator.pop(context); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/auth/presentation/pages/woman/woman_verification_Page.dart b/lib/auth/presentation/pages/woman/woman_verification_Page.dart new file mode 100644 index 0000000..4370524 --- /dev/null +++ b/lib/auth/presentation/pages/woman/woman_verification_Page.dart @@ -0,0 +1,273 @@ +import 'dart:developer'; + +import 'package:code_l/auth/presentation/pages/woman/providers.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:http/http.dart' as ref; + +import '../../../../core/utills/design/app_colors.dart'; +import '../../../../core/utills/design/app_gaps.dart'; +import '../../../../core/utills/design/app_typography.dart'; +import '../../../../sign_up/presentation/widgets/sign_up_confirm_button.dart'; +import '../congratulation/congratulation_page.dart'; +import 'widgets/woman_verification_app_bar.dart'; +import '../login/login_page.dart'; + +class WomanVerificationPage extends ConsumerWidget { + const WomanVerificationPage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final viewModel = ref.watch(womanViewmodelProvider); + final notifier = ref.read(womanViewmodelProvider.notifier); + return Scaffold( + appBar: WomanVerificationAppBar(), + bottomNavigationBar: Padding( + padding: const EdgeInsets.all(AppGaps.gap20), + child: ConfirmButton( + enabled: true, + onPressed: () { + _buildBottomSheetField(context, viewModel, notifier); + }, + ), + ), + body: _buildInformationField(), + ); + } + + Future _buildBottomSheetField(context, viewModel, notifier) async { + return showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return Consumer( + builder: (context, ref, _) { + final viewModel = ref.watch(womanViewmodelProvider); + final notifier = ref.read(womanViewmodelProvider.notifier); + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(AppGaps.gap20), + topRight: Radius.circular(AppGaps.gap20), + ), + color: AppColors.white, + ), + height: MediaQuery.sizeOf(context).height * 0.5, + child: Center( + child: Padding( + padding: const EdgeInsets.all(AppGaps.gap20), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "꼭 확인해 주세요", + style: AppTypography.subtitle2.copyWith( + color: AppColors.grey900, + ), + ), + IconButton( + onPressed: () => Navigator.pop(context), + icon: Icon(Icons.close), + ), + ], + ), + SizedBox(height: AppGaps.gap20), + Flexible( + child: Text( + "code:L은 여러분의 안전을 소중히 생각합니다.\n해당 확인서는 여성들만의 안전한 커뮤니티를 함께 지키기 위한 약속입니다.", + style: AppTypography.subtitle2.copyWith( + color: AppColors.grey900, + ), + ), + ), + SizedBox(height: AppGaps.gap20), + Row( + children: [ + Text( + "1", + style: AppTypography.subtitle3.copyWith( + color: AppColors.grey800, + ), + ), + SizedBox(width: AppGaps.gap16), + Flexible( + child: Text( + "본 서비스가 여성들끼리의 소중한 인연을 위한 플랫폼임을 이해했으며, 여성 사용자임을 확인합니다.", + style: AppTypography.body2.copyWith( + color: AppColors.grey800, + ), + ), + ), + ], + ), + SizedBox(height: AppGaps.gap20), + Row( + children: [ + Text( + "2", + style: AppTypography.subtitle3.copyWith( + color: AppColors.grey800, + ), + ), + SizedBox(width: AppGaps.gap16), + Flexible( + child: Text( + "여성이 아님에도 가입할 경우 발생하는 모든 법적 책임은 사용자 본인에게 있습니다.", + style: AppTypography.body2.copyWith( + color: AppColors.grey800, + ), + ), + ), + ], + ), + SizedBox(height: AppGaps.gap20), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "위 안내사항을 모두 확인하였습니다.", + style: AppTypography.subtitle2.copyWith( + color: viewModel.isChecked + ? AppColors.grey800 + : AppColors.grey400, + ), + ), + IconButton( + onPressed: () { + notifier.toggle(); + }, + icon: Icon( + Icons.check_circle_outline, + color: viewModel.isChecked + ? AppColors.grey800 + : AppColors.grey400, + ), + ), + ], + ), + SizedBox(height: AppGaps.gap20), + ConfirmButton( + enabled: viewModel.isChecked, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => + const CongratulationPage(), + ), + ); + }, + ), + ], + ), + ), + ), + ); + }, + ); + }); + } + + Widget _buildInformationField() { + return SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: AppGaps.gap20), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 30), + SvgPicture.asset("assets/icons/logo.svg"), + SizedBox(height: AppGaps.gap12), + Row( + children: [ + Text( + "레즈비언", + style: AppTypography.header2.copyWith( + color: AppColors.primary, + ), + ), + Text("을 위한", style: AppTypography.header2), + ], + ), + Text( + "더 깊이있는 매칭 공간", + style: AppTypography.header2.copyWith(color: AppColors.grey900), + ), + SizedBox(height: AppGaps.gap36), + Text( + "CODE:L은 여성들이 서로의 마음을 나누고,\n인연을 찾을 수 있는 안전한 커뮤니티입니다.\n", + style: AppTypography.body1, + ), + Text( + "같은 마음을 가진 사람들과 더 안전하고 편안하게\n소통할 수 있는 공간을 만들기 위해 다음 규칙을\n꼭 지켜주세요.", + style: AppTypography.body1, + ), + SizedBox(height: AppGaps.gap36), + Row( + children: [ + Icon(Icons.favorite, color: AppColors.error), + SizedBox(width: AppGaps.gap8), + Text("여성 사용자만 가입 가능합니다.", style: AppTypography.subtitle3), + ], + ), + Row( + children: [ + Icon(Icons.favorite, color: AppColors.error), + SizedBox(width: AppGaps.gap8), + Text("모든 프로필은 심사 후 승인됩니다.", style: AppTypography.subtitle3), + ], + ), + Row( + children: [ + Icon(Icons.favorite, color: AppColors.error), + SizedBox(width: AppGaps.gap8), + Text("서로를 존중하는 대화를 지향합니다.", style: AppTypography.subtitle3), + ], + ), + Row( + children: [ + Icon(Icons.favorite, color: AppColors.error), + SizedBox(width: AppGaps.gap8), + Text( + "코드가 맞는 사람과 깊이 있는 연결을 만들어 갑니다.", + style: AppTypography.subtitle3, + ), + ], + ), + SizedBox(height: AppGaps.gap36), + Container( + height: 114, + width: double.infinity, + alignment: Alignment.center, + color: AppColors.grey100, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: AppGaps.gap16, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("💌운영진의 노력", style: AppTypography.caption1), + SizedBox(height: AppGaps.gap8), + Flexible( + child: Text( + "CODE 가 맞는 사람들의 진심 어린 연결을 위해 운영진은\n언제나 보이지 않는 곳에서 신중히 고민하고 정성스럽게 공간을\n지켜가고 있어요.", + style: AppTypography.caption1.copyWith( + color: AppColors.grey700, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/auth/presentation/pages/woman/woman_verification_viewmodel.dart b/lib/auth/presentation/pages/woman/woman_verification_viewmodel.dart new file mode 100644 index 0000000..3a26a5e --- /dev/null +++ b/lib/auth/presentation/pages/woman/woman_verification_viewmodel.dart @@ -0,0 +1,13 @@ +import 'dart:developer'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class WomanVerificationViewmodel extends ChangeNotifier { + bool isChecked = false; + + void toggle() { + isChecked = !isChecked; + notifyListeners(); + } +} diff --git a/lib/auth/presentation/widgets/.gitkeep b/lib/auth/presentation/widgets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/auth/presentation/widgets/auth_confirm_button.dart b/lib/auth/presentation/widgets/auth_confirm_button.dart new file mode 100644 index 0000000..4e41969 --- /dev/null +++ b/lib/auth/presentation/widgets/auth_confirm_button.dart @@ -0,0 +1,40 @@ +import 'package:flutter/cupertino.dart'; + +import '../../../core/utills/design/app_colors.dart'; +import '../../../core/utills/design/app_typography.dart'; + +class AuthConfirmButton extends StatelessWidget { + const AuthConfirmButton({ + super.key, + required this.enabled, + this.onPressed, + this.text = "확인", + }); + + final bool enabled; + final VoidCallback? onPressed; + final String text; + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: enabled ? onPressed : null, + child: Container( + height: 54, + width: double.infinity, + padding: EdgeInsets.symmetric(vertical: 16), + alignment: Alignment.center, + decoration: BoxDecoration( + color: enabled ? AppColors.primary : AppColors.grey200, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + text, + style: AppTypography.subtitle2.copyWith( + color: enabled ? AppColors.white : AppColors.grey400, + ), + ), + ), + ); + } +} diff --git a/lib/core/utills/design/app_colors.dart b/lib/core/utills/design/app_colors.dart index d28487a..0ae9df5 100644 --- a/lib/core/utills/design/app_colors.dart +++ b/lib/core/utills/design/app_colors.dart @@ -13,8 +13,9 @@ class AppColors { static const grey400 = Color(0xFFCCCCCC); static const grey500 = Color(0xFFB0B0B0); static const grey600 = Color(0xFF999999); - static const grey700 = Color(0xFF333333); - static const grey800 = Color(0xFF222222); + static const grey700 = Color(0xFF666666); + static const grey800 = Color(0xFF333333); + static const grey900 = Color(0xFF222222); static const error = Color(0xFFEF2B2A); static const success = Color(0xFF27C937); static const warning = Color(0xFFFFAA00); diff --git a/lib/main.dart b/lib/main.dart index d2dee9d..335e24e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:code_l/auth/presentation/pages/login/login_page.dart'; +import 'package:code_l/auth/presentation/pages/terms_and_conditions/terms_and_condition_page.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; @@ -25,7 +26,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Kakao Login Demo', - home: const LoginPage(), + home: const TermsAndConditionPage(), theme: ThemeData(primarySwatch: Colors.blue, scaffoldBackgroundColor: Colors.white, ), diff --git a/lib/sign_up/presentation/pages/code_age/code_age_page.dart b/lib/sign_up/presentation/pages/code_age/code_age_page.dart index e9795b0..2a6e6f5 100644 --- a/lib/sign_up/presentation/pages/code_age/code_age_page.dart +++ b/lib/sign_up/presentation/pages/code_age/code_age_page.dart @@ -17,58 +17,66 @@ class CodeAgePage extends StatelessWidget { final isValid = true; // todo 상태관리 return Scaffold( - appBar: SignUpAppBar(), + appBar: SignUpAppBar(), bottomNavigationBar: Padding( padding: const EdgeInsets.all(AppGaps.gap20), - child: ConfirmButton(enabled: isValid,onPressed: () { Navigator.push( - context, - MaterialPageRoute(builder: (context) => CodeJobPage()), - );}, ), + child: ConfirmButton( + enabled: isValid, + onPressed: () { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => CodeJobPage()), + ); + }, + ), ), - body: SafeArea(child: Padding( padding: const EdgeInsets.symmetric(horizontal: AppGaps.gap24,vertical: AppGaps.gap40), - - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox(height: screenHeight * 0.05), - Text("나이를", style: AppTypography.header1), - Text("입력해주세요", style: AppTypography.header1), - SizedBox(height: AppGaps.gap12), - SizedBox(height: AppGaps.gap40 + AppGaps.gap40 + AppGaps.gap40), - - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextField( - decoration: InputDecoration( - hintText: '나이를 입력해주세요', - hintStyle: AppTypography.header2.copyWith(color: AppColors.grey400), - contentPadding: EdgeInsets.symmetric(vertical: AppGaps.gap8), - border: UnderlineInputBorder( - borderSide: BorderSide(color: AppColors.grey400, width: 0.6), - ), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: AppColors.grey400, width: 0.6), - ), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide( - color: AppColors.grey400, - width: 0.6, + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: AppGaps.gap24, vertical: AppGaps.gap40), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: screenHeight * 0.05), + Text("나이를", style: AppTypography.header1), + Text("입력해주세요", style: AppTypography.header1), + SizedBox(height: AppGaps.gap12), + SizedBox(height: AppGaps.gap40 + AppGaps.gap40 + AppGaps.gap40), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + decoration: InputDecoration( + hintText: '나이를 입력해주세요', + hintStyle: AppTypography.header2 + .copyWith(color: AppColors.grey400), + contentPadding: + EdgeInsets.symmetric(vertical: AppGaps.gap8), + border: UnderlineInputBorder( + borderSide: + BorderSide(color: AppColors.grey400, width: 0.6), + ), + enabledBorder: UnderlineInputBorder( + borderSide: + BorderSide(color: AppColors.grey400, width: 0.6), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide( + color: AppColors.grey400, + width: 0.6, + ), + ), + suffixIcon: IconButton( + icon: Icon(Icons.clear, size: 16), + onPressed: () => {/*todo 삭제버튼*/}), + ), ), - ), - suffixIcon: IconButton( - icon: Icon(Icons.clear, size: 16), - onPressed: () => {/*todo 삭제버튼*/} - ), + SizedBox(height: AppGaps.gap4), + ], ), - ), - SizedBox(height: AppGaps.gap4), - ], + ], + ), ), - ], - ), - ) - ,) - ); + )); } } diff --git a/lib/sign_up/presentation/pages/code_job/code_job_page.dart b/lib/sign_up/presentation/pages/code_job/code_job_page.dart index 66aa283..312ff63 100644 --- a/lib/sign_up/presentation/pages/code_job/code_job_page.dart +++ b/lib/sign_up/presentation/pages/code_job/code_job_page.dart @@ -15,7 +15,7 @@ class CodeJobPage extends ConsumerStatefulWidget { ConsumerState createState() => _JobSelectionPageState(); } -class _JobSelectionPageState extends ConsumerState{ +class _JobSelectionPageState extends ConsumerState { String? selectedJob; @override @@ -46,7 +46,8 @@ class _JobSelectionPageState extends ConsumerState{ ), body: SafeArea( child: Padding( - padding: const EdgeInsets.symmetric(horizontal: AppGaps.gap24,vertical: AppGaps.gap40), + padding: const EdgeInsets.symmetric( + horizontal: AppGaps.gap24, vertical: AppGaps.gap40), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -89,7 +90,8 @@ class _JobSelectionPageState extends ConsumerState{ height: 22, ), SizedBox(height: AppGaps.gap4), - Text(job['label'] ?? '', style: AppTypography.body2), + Text(job['label'] ?? '', + style: AppTypography.body2), ], ), ), diff --git a/lib/sign_up/presentation/pages/code_name/code_name_page.dart b/lib/sign_up/presentation/pages/code_name/code_name_page.dart index 21d0d83..b155be3 100644 --- a/lib/sign_up/presentation/pages/code_name/code_name_page.dart +++ b/lib/sign_up/presentation/pages/code_name/code_name_page.dart @@ -56,7 +56,8 @@ Widget _buildHeaderText() { ); } -Widget _buildCodeNameInputField(bool isValid, TextEditingController controller) { +Widget _buildCodeNameInputField( + bool isValid, TextEditingController controller) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -83,9 +84,7 @@ Widget _buildCodeNameInputField(bool isValid, TextEditingController controller) ), SizedBox(height: AppGaps.gap4), Text( - isValid - ? '멋진 코드네임이네요!' - : '한글, 숫자 최대 10글자까지 사용 가능합니다', + isValid ? '멋진 코드네임이네요!' : '한글, 숫자 최대 10글자까지 사용 가능합니다', style: AppTypography.caption2.copyWith( color: isValid ? AppColors.success : AppColors.error, ), @@ -93,5 +92,3 @@ Widget _buildCodeNameInputField(bool isValid, TextEditingController controller) ], ); } - - diff --git a/lib/sign_up/presentation/widgets/.gitkeep b/lib/sign_up/presentation/widgets/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/sign_up/presentation/widgets/sign_up_app_bar.dart b/lib/sign_up/presentation/widgets/sign_up_app_bar.dart index 75e6de9..cad93d3 100644 --- a/lib/sign_up/presentation/widgets/sign_up_app_bar.dart +++ b/lib/sign_up/presentation/widgets/sign_up_app_bar.dart @@ -1,4 +1,3 @@ - import 'package:code_l/core/utills/design/app_colors.dart'; import 'package:flutter/material.dart'; @@ -24,28 +23,28 @@ class SignUpAppBar extends StatelessWidget implements PreferredSizeWidget { ), // 진행률 - Expanded( - child: Container( - height: 6, - margin: EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - color: AppColors.primaryLight, // 배경 (전체 바) - borderRadius: BorderRadius.circular(2), - ), - child: FractionallySizedBox( - alignment: Alignment.centerLeft, - widthFactor: progress, // 진행률 - child: Container( - decoration: BoxDecoration( - color: AppColors.primary, - borderRadius: BorderRadius.circular(2), - ), - ), - ), - ), - ), + Expanded( + child: Container( + height: 6, + margin: EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + color: AppColors.primaryLight, // 배경 (전체 바) + borderRadius: BorderRadius.circular(2), + ), + child: FractionallySizedBox( + alignment: Alignment.centerLeft, + widthFactor: progress, // 진행률 + child: Container( + decoration: BoxDecoration( + color: AppColors.primary, + borderRadius: BorderRadius.circular(2), + ), + ), + ), + ), + ), - // 닫기 버튼 + // 닫기 버튼 IconButton( icon: Icon(Icons.close), onPressed: () { diff --git a/lib/sign_up/presentation/widgets/sign_up_confirm_button.dart b/lib/sign_up/presentation/widgets/sign_up_confirm_button.dart index a73ab39..e4df7d4 100644 --- a/lib/sign_up/presentation/widgets/sign_up_confirm_button.dart +++ b/lib/sign_up/presentation/widgets/sign_up_confirm_button.dart @@ -28,12 +28,10 @@ class ConfirmButton extends StatelessWidget { color: enabled ? AppColors.primary : AppColors.grey200, borderRadius: BorderRadius.circular(8), ), - child: Text( - text, + child: Text(text, style: AppTypography.subtitle2.copyWith( color: enabled ? AppColors.white : AppColors.grey400, - ) - ), + )), ), ); }