diff --git a/lib/sign_up/domain/model/profile_mbti/profile_mbti_state.dart b/lib/sign_up/domain/model/profile_mbti/profile_mbti_state.dart new file mode 100644 index 0000000..7ae2731 --- /dev/null +++ b/lib/sign_up/domain/model/profile_mbti/profile_mbti_state.dart @@ -0,0 +1,16 @@ +class MBTIState { + final String? ei, ns, tf, pj; + + const MBTIState({this.ei, this.ns, this.tf, this.pj}); + + MBTIState copyWith({String? ei, String? ns, String? tf, String? pj}) { + return MBTIState( + ei: ei ?? this.ei, + ns: ns ?? this.ns, + tf: tf ?? this.tf, + pj: pj ?? this.pj, + ); + } + + bool get isValid => ei != null && ns != null && tf != null && pj != null; +} 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 new file mode 100644 index 0000000..7b8a276 --- /dev/null +++ b/lib/sign_up/presentation/pages/profile_mbti/profile_mbti_page.dart @@ -0,0 +1,120 @@ +import 'package:code_l/sign_up/presentation/pages/profile_mbti/providers.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 '../../widgets/sign_up_app_bar.dart'; +import '../../widgets/sign_up_confirm_button.dart'; + +class ProfileMBTIPage extends ConsumerWidget { + const ProfileMBTIPage({super.key}); + + final mbtiOptions = const [ + ['E', 'I'], + ['N', 'S'], + ['T', 'F'], + ['P', 'J'], + ]; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final screenHeight = MediaQuery.of(context).size.height; + final mbtiState = ref.watch(profileMBTIProvider); + final viewModel = ref.read(profileMBTIProvider.notifier); + + return Scaffold( + appBar: const SignUpAppBar(), + bottomNavigationBar: Padding( + padding: const EdgeInsets.symmetric( + horizontal: AppGaps.gap20, + vertical: 34, + ), + child: ConfirmButton( + enabled: mbtiState.isValid, + onPressed: () { + // TODO: 다음 페이지 이동 로직 + }, + ), + ), + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: AppGaps.gap20), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: screenHeight * 0.05), + Text("MBTI를\n선택해주세요", style: AppTypography.header1), + const SizedBox(height: AppGaps.gap40), + + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: mbtiOptions.length, + itemBuilder: (context, groupIndex) { + final group = mbtiOptions[groupIndex]; + return Padding( + padding: const EdgeInsets.only(bottom: AppGaps.gap20), + child: Row( + children: + group.map((option) { + final isSelected = viewModel.isSelected( + groupIndex, + option, + ); + return Expanded( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 6, + ), + child: GestureDetector( + onTap: + () => viewModel.select( + groupIndex, + option, + ), + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 74, + vertical: 34, + ), + decoration: BoxDecoration( + color: + isSelected + ? AppColors.primaryLight + : AppColors.grey100, + border: Border.all( + color: + isSelected + ? AppColors.primary + : AppColors.grey100, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Text( + option, + style: AppTypography.body1.copyWith( + color: AppColors.grey900, + ), + ), + ), + ), + ), + ), + ); + }).toList(), + ), + ); + }, + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/sign_up/presentation/pages/profile_mbti/profile_mbti_viewmodel.dart b/lib/sign_up/presentation/pages/profile_mbti/profile_mbti_viewmodel.dart new file mode 100644 index 0000000..ccbc8b0 --- /dev/null +++ b/lib/sign_up/presentation/pages/profile_mbti/profile_mbti_viewmodel.dart @@ -0,0 +1,27 @@ +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../domain/model/profile_mbti/profile_mbti_state.dart'; + +class ProfileMBTIViewModel extends StateNotifier { + ProfileMBTIViewModel() : super(const MBTIState()); + + void select(int groupIndex, String value) { + state = switch (groupIndex) { + 0 => state.copyWith(ei: value), + 1 => state.copyWith(ns: value), + 2 => state.copyWith(tf: value), + 3 => state.copyWith(pj: value), + _ => state, + }; + } + + bool isSelected(int groupIndex, String value) { + return switch (groupIndex) { + 0 => state.ei == value, + 1 => state.ns == value, + 2 => state.tf == value, + 3 => state.pj == value, + _ => false, + }; + } +} diff --git a/lib/sign_up/presentation/pages/profile_mbti/providers.dart b/lib/sign_up/presentation/pages/profile_mbti/providers.dart new file mode 100644 index 0000000..90f47cd --- /dev/null +++ b/lib/sign_up/presentation/pages/profile_mbti/providers.dart @@ -0,0 +1,9 @@ +import 'package:code_l/sign_up/presentation/pages/profile_mbti/profile_mbti_viewmodel.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../domain/model/profile_mbti/profile_mbti_state.dart'; + +final profileMBTIProvider = + StateNotifierProvider( + (ref) => ProfileMBTIViewModel(), + );