diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 001df22..b7504a3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,3 +13,4 @@ close # ## 💬리뷰 요구사항 +## 다음 작업 계획 diff --git a/lib/sign_up/presentation/pages/profile_introduce/profile_introduce_page.dart b/lib/sign_up/presentation/pages/profile_introduce/profile_introduce_page.dart new file mode 100644 index 0000000..10009b3 --- /dev/null +++ b/lib/sign_up/presentation/pages/profile_introduce/profile_introduce_page.dart @@ -0,0 +1,107 @@ +import 'package:code_l/core/utills/design/app_colors.dart'; +import 'package:code_l/core/utills/design/app_gaps.dart'; +import 'package:code_l/core/utills/design/app_typography.dart'; +import 'package:code_l/sign_up/presentation/pages/profile_introduce/providers.dart'; +import 'package:flutter/material.dart'; + +import '../../widgets/sign_up_app_bar.dart'; +import '../../widgets/sign_up_confirm_button.dart'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../profile_image/profile_image_page.dart'; +import 'profile_introduce_viewmodel.dart'; + +class ProfileIntroducePage extends ConsumerWidget { + const ProfileIntroducePage({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final screenHeight = MediaQuery.of(context).size.height; + final state = ref.watch(profileIntroduceProvider); + final notifier = ref.read(profileIntroduceProvider.notifier); + + return Scaffold( + appBar: SignUpAppBar(currentPage: 10), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.all(AppGaps.gap24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: screenHeight * 0.05), + _buildHeaderText(), + SizedBox(height: AppGaps.gap40 + AppGaps.gap40), + _buildCodeNameInputField(state.introduction, notifier), + Spacer(), + ConfirmButton( + enabled: state.isValid, + onPressed: + state.isValid + ? () => { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ProfileImagePage(), + ), + ), + } + : null, + ), + ], + ), + ), + ), + ); + } + + Widget _buildHeaderText() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("간단하게", style: AppTypography.header1), + Text("나를 소개해주세요", style: AppTypography.header1), + SizedBox(height: AppGaps.gap12), + Text( + "자기소개로 다른 사람에게 나를 어필해보세요!", + style: AppTypography.body2.copyWith(color: AppColors.grey600), + ), + ], + ); + } + + Widget _buildCodeNameInputField( + String introduction, + ProfileIntroduceViewmodel notifier, + ) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextField( + onChanged: notifier.updateIntroduction, + 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), + ), + focusedBorder: UnderlineInputBorder( + borderSide: BorderSide(color: AppColors.grey400, width: 0.6), + ), + ), + ), + SizedBox(height: AppGaps.gap4), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text(introduction.length.toString(), style: AppTypography.caption3), + Text( + " / 15", + style: AppTypography.caption3.copyWith(color: AppColors.grey500), + ), + ], + ), + ], + ); + } +} diff --git a/lib/sign_up/presentation/pages/profile_introduce/profile_introduce_viewmodel.dart b/lib/sign_up/presentation/pages/profile_introduce/profile_introduce_viewmodel.dart new file mode 100644 index 0000000..e23e02f --- /dev/null +++ b/lib/sign_up/presentation/pages/profile_introduce/profile_introduce_viewmodel.dart @@ -0,0 +1,32 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +class ProfileIntroduceState { + final String introduction; + final bool isValid; + + ProfileIntroduceState({ + this.introduction = '', + this.isValid = false, + }); + + ProfileIntroduceState copyWith({ + String? introduction, + bool? isValid, + }) { + return ProfileIntroduceState( + introduction: introduction ?? this.introduction, + isValid: isValid ?? this.isValid, + ); + } +} + +class ProfileIntroduceViewmodel extends StateNotifier { + ProfileIntroduceViewmodel() : super(ProfileIntroduceState()); + + void updateIntroduction(String introduction) { + state = state.copyWith( + introduction: introduction, + isValid: introduction.isNotEmpty && introduction.length <= 15, + ); + } +} diff --git a/lib/sign_up/presentation/pages/profile_introduce/providers.dart b/lib/sign_up/presentation/pages/profile_introduce/providers.dart new file mode 100644 index 0000000..1be260c --- /dev/null +++ b/lib/sign_up/presentation/pages/profile_introduce/providers.dart @@ -0,0 +1,6 @@ +import 'package:code_l/sign_up/presentation/pages/profile_introduce/profile_introduce_viewmodel.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +final profileIntroduceProvider = + StateNotifierProvider( + (ref) => ProfileIntroduceViewmodel()); diff --git a/lib/sign_up/presentation/pages/profile_mbti/profile_mbti_page.dart b/lib/sign_up/presentation/pages/profile_mbti/profile_mbti_page.dart index 3a4d2b4..2e3d9ab 100644 --- a/lib/sign_up/presentation/pages/profile_mbti/profile_mbti_page.dart +++ b/lib/sign_up/presentation/pages/profile_mbti/profile_mbti_page.dart @@ -1,3 +1,4 @@ +import 'package:code_l/sign_up/presentation/pages/profile_introduce/profile_introduce_page.dart'; import 'package:code_l/sign_up/presentation/pages/profile_mbti/providers.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -34,7 +35,11 @@ class ProfileMBTIPage extends ConsumerWidget { child: ConfirmButton( enabled: mbtiState.isValid, onPressed: () { - // TODO: 다음 페이지 이동 로직 + Navigator.push( + context, + MaterialPageRoute(builder: + (context) => ProfileIntroducePage()), + ); }, ), ),