diff --git a/economic_fe/.vscode/settings.json b/economic_fe/.vscode/settings.json new file mode 100644 index 0000000..385f27a --- /dev/null +++ b/economic_fe/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} diff --git a/economic_fe/android/app/build.gradle b/economic_fe/android/app/build.gradle index ed44bfd..2a713a9 100644 --- a/economic_fe/android/app/build.gradle +++ b/economic_fe/android/app/build.gradle @@ -16,6 +16,15 @@ plugins { id "dev.flutter.flutter-gradle-plugin" } +def dotenvFile = rootProject.file('.env') +def dotenvProps = new Properties() + +if (dotenvFile.exists()) { + dotenvFile.withReader { reader -> + dotenvProps.load(reader) + } +} + android { namespace = "com.example.economic_fe" compileSdk = flutter.compileSdkVersion @@ -39,7 +48,10 @@ android { targetSdk = flutter.targetSdkVersion versionCode = flutter.versionCode versionName = flutter.versionName - manifestPlaceholders += [KAKAO_NATIVE_APP_KEY: project.env.get("NATIVE_APP_KEY")] + // manifestPlaceholders += [KAKAO_NATIVE_APP_KEY: project.env.get("NATIVE_APP_KEY")] + manifestPlaceholders += [ + KAKAO_NATIVE_APP_KEY: dotenvProps['NATIVE_APP_KEY'] ?: "default_key" + ] } buildTypes { diff --git a/economic_fe/lib/data/models/user_model.dart b/economic_fe/lib/data/models/user_model.dart deleted file mode 100644 index 4a79928..0000000 --- a/economic_fe/lib/data/models/user_model.dart +++ /dev/null @@ -1,9 +0,0 @@ -// -// -// -//유저 모델 -// -// - -import 'dart:convert'; -import 'package:get/get.dart'; diff --git a/economic_fe/lib/data/models/user_profile.dart b/economic_fe/lib/data/models/user_profile.dart new file mode 100644 index 0000000..11837f0 --- /dev/null +++ b/economic_fe/lib/data/models/user_profile.dart @@ -0,0 +1,34 @@ +class UserProfile { + String nickname; + String businessType; + String job; + String ageRange; + String gender; + String? profileIntro; + bool isLearningAlarmAllowed; + bool isCommunityAlarmAllowed; + + UserProfile({ + required this.nickname, + required this.businessType, + required this.job, + required this.ageRange, + required this.gender, + this.profileIntro, + this.isLearningAlarmAllowed = false, + this.isCommunityAlarmAllowed = false, + }); + + Map toJson() { + return { + 'nickname': nickname, + 'businessType': businessType, + 'job': job, + 'ageRange': ageRange, + 'gender': gender, + 'profileIntro': profileIntro, + 'isLearningAlarmAllowed': isLearningAlarmAllowed, + 'isCommunityAlarmAllowed': isCommunityAlarmAllowed, + }; + } +} diff --git a/economic_fe/lib/data/services/remote_data_source.dart b/economic_fe/lib/data/services/remote_data_source.dart index dd65d7f..52d7f58 100644 --- a/economic_fe/lib/data/services/remote_data_source.dart +++ b/economic_fe/lib/data/services/remote_data_source.dart @@ -9,26 +9,36 @@ class RemoteDataSource { /// API POST /// /// 데이터 생성시 사용 - static Future _postApi(String endPoint, String? jsonData) async { + /// authToken을 포함하도록 수정 + static Future postApi( + String endPoint, + Map jsonData, + ) async { String apiUrl = '$baseUrl/$endPoint'; - Map headers = {'Content-Type': 'application/json'}; - // String requestBody = jsonData; - debugPrint('POST 요청: $endPoint'); + String authToken = dotenv.env['AUTHORIZATION_KEY']!; // 환경 변수에서 가져오기 + Map headers = { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer $authToken', + }; try { - final response = - await http.post(Uri.parse(apiUrl), headers: headers, body: jsonData); + final response = await http.post( + Uri.parse(apiUrl), + headers: headers, + body: jsonEncode(jsonData), + ); + if (response.statusCode == 200) { debugPrint('POST 요청 성공'); + return response.statusCode; } else { - debugPrint('POST 요청 실패: (${response.statusCode})${response.body}'); + debugPrint('POST 요청 실패: (${response.statusCode}) ${response.body}'); } - // 예외 처리를 위한 status code 반환 return response.statusCode; } catch (e) { debugPrint('POST 요청 중 예외 발생: $e'); - return; + return null; } } diff --git a/economic_fe/lib/data/services/user_router.dart b/economic_fe/lib/data/services/user_router.dart index a247320..0452d62 100644 --- a/economic_fe/lib/data/services/user_router.dart +++ b/economic_fe/lib/data/services/user_router.dart @@ -35,82 +35,6 @@ import 'package:economic_fe/view/screens/test_page.dart'; import 'package:get/get.dart'; import 'package:get/get_navigation/src/routes/get_route.dart'; -// class UserRouter { -// static GoRouter getRouter() { -// return GoRouter( -// initialLocation: '/', // 초기 경로 설정 -// routes: [ -// GoRoute( -// path: '/', -// builder: (context, state) => const OnboardingPage(), // 홈 페이지 -// ), -// GoRoute( -// path: '/onboarding', -// builder: (context, state) => const OnboardingCardPage(), -// ), -// GoRoute( -// path: '/test', -// builder: (context, state) => const TestPage(), -// routes: [ -// GoRoute( -// path: 'multi', -// builder: (context, state) => const TestMultipleChoicePage(), -// ), -// GoRoute( -// path: 'ox', -// builder: (context, state) => const TestOxPage(), -// ), -// ], -// ), -// GoRoute( -// path: '/leveltest_result', -// builder: (context, state) => const LeveltestResultPage(), -// ), -// GoRoute( -// path: '/login', -// builder: (context, state) => const LoginPage(), -// ), -// GoRoute( -// path: '/profile_setting', -// builder: (context, state) => const ProfileSettingPage(), -// routes: [ -// GoRoute( -// path: 'basic', -// builder: (context, state) => const BasicInfoPage(), -// ), -// GoRoute( -// path: 'job', -// builder: (context, state) => const JobSelectPage(), -// ), -// GoRoute( -// path: 'part', -// builder: (context, state) => const PartSelectPage(), -// ), -// ], -// ), -// GoRoute( -// path: '/home', -// builder: (context, state) => const HomePage(), -// ), -// GoRoute( -// path: '/learning_list', -// builder: (context, state) => const LearningListPage(), -// routes: [ -// GoRoute( -// path: 'quiz_level', -// builder: (context, state) => const LevelSelectPage(), -// routes: [ -// GoRoute( -// path: 'quiz', -// builder: (context, state) => const QuizPage(), -// ) -// ]) -// ]), -// ], -// ); -// } -// } - // GetX의 라우터를 사용하는 방식 class UserRouter { static List getPages() { diff --git a/economic_fe/lib/main.dart b/economic_fe/lib/main.dart index 4466338..c496ead 100644 --- a/economic_fe/lib/main.dart +++ b/economic_fe/lib/main.dart @@ -1,10 +1,7 @@ import 'package:economic_fe/data/services/user_router.dart'; -import 'package:economic_fe/view/screens/onboarding/onboarding_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:get/get.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:flutter/material.dart'; import 'package:kakao_flutter_sdk_user/kakao_flutter_sdk_user.dart'; Future main() async { @@ -17,38 +14,6 @@ Future main() async { runApp(const RippleApp()); } -// class MyApp extends StatelessWidget { -// const MyApp({super.key}); - -// // This widget is the root of your application. -// @override -// Widget build(BuildContext context) { -// // return GetMaterialApp( -// // title: '경제 지식 앱', -// // theme: ThemeData( -// // primarySwatch: Colors.blue, -// // ), -// // home: const Text("test11"), -// // ); -// // return OnboardingPage(); -// // return const MaterialApp( -// // home: OnboardingPage(), -// // ); -// runApp(const RippleApp()); -// } -// } - -// class RippleApp extends StatelessWidget { -// const RippleApp({super.key}); - -// @override -// Widget build(BuildContext context) { -// return MaterialApp.router( -// routerConfig: UserRouter.getRouter(), -// ); -// } -// } - class RippleApp extends StatelessWidget { const RippleApp({super.key}); diff --git a/economic_fe/lib/view/screens/community/community_page.dart b/economic_fe/lib/view/screens/community/community_page.dart index 119cf90..059fb99 100644 --- a/economic_fe/lib/view/screens/community/community_page.dart +++ b/economic_fe/lib/view/screens/community/community_page.dart @@ -29,7 +29,7 @@ class CommunityPage extends StatelessWidget { child: GestureDetector( onTap: () => controller.toTalkDetailPage(), child: Container( - width: 328, + width: MediaQuery.of(context).size.width - 32, height: 122, decoration: ShapeDecoration( image: DecorationImage( @@ -587,21 +587,24 @@ class TalkListItem extends StatelessWidget { @override Widget build(BuildContext context) { return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // 경제톡톡 이미지 - GestureDetector( - onTap: onTap, - child: Container( - width: 97, - height: 118, - decoration: ShapeDecoration( - image: const DecorationImage( - image: AssetImage('assets/talk_image_sample.png'), - fit: BoxFit.cover, - ), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(7), + Padding( + padding: const EdgeInsets.only(right: 12), + child: GestureDetector( + onTap: onTap, + child: Container( + width: 97, + height: 118, + decoration: ShapeDecoration( + image: const DecorationImage( + image: AssetImage('assets/talk_image_sample.png'), + fit: BoxFit.cover, + ), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(7), + ), ), ), ), @@ -610,9 +613,9 @@ class TalkListItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox( - width: 219, - child: Row( + SizedBox( + width: MediaQuery.of(context).size.width - (32 + 97 + 12), + child: const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( @@ -644,10 +647,10 @@ class TalkListItem extends StatelessWidget { ), GestureDetector( onTap: onTap, - child: const SizedBox( - width: 219, + child: SizedBox( + width: MediaQuery.of(context).size.width - (32 + 97 + 12), height: 60, - child: Flexible( + child: const Flexible( child: Text( '현재 경제 상황에서 가장 중요한 투자 전략은 무엇이라고 생각하나요? 현재 경제 상황에서 가장 중요한 투자 전략...', style: TextStyle( diff --git a/economic_fe/lib/view/screens/community/talk_detail_page.dart b/economic_fe/lib/view/screens/community/talk_detail_page.dart index c45a884..c7c07f9 100644 --- a/economic_fe/lib/view/screens/community/talk_detail_page.dart +++ b/economic_fe/lib/view/screens/community/talk_detail_page.dart @@ -1,8 +1,6 @@ -import 'package:economic_fe/data/models/community/comment.dart'; import 'package:economic_fe/view/theme/palette.dart'; import 'package:economic_fe/view/widgets/chatbot_fab.dart'; import 'package:economic_fe/view/widgets/community/comment_widget.dart'; -import 'package:economic_fe/view/widgets/custom_app_bar.dart'; import 'package:economic_fe/view_model/community/talk_detail_controller.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -40,16 +38,6 @@ class TalkDetailPage extends StatelessWidget { ), ), centerTitle: true, - actions: [ - // 더보기 버튼 - Padding( - padding: const EdgeInsets.only(right: 16), - child: GestureDetector( - onTap: () {}, - child: const Icon(Icons.more_horiz), - ), - ), - ], ), body: Stack( children: [ diff --git a/economic_fe/lib/view/screens/dictionary_page.dart b/economic_fe/lib/view/screens/dictionary_page.dart index 3be1043..1c39d03 100644 --- a/economic_fe/lib/view/screens/dictionary_page.dart +++ b/economic_fe/lib/view/screens/dictionary_page.dart @@ -1,11 +1,9 @@ import 'package:economic_fe/view/theme/palette.dart'; import 'package:economic_fe/view/widgets/custom_app_bar.dart'; import 'package:economic_fe/view/widgets/custom_bottom_bar.dart'; -import 'package:economic_fe/view_model/login/agreement_controller.dart'; import 'package:economic_fe/view_model/dictionary_controller.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; class DictionaryPage extends StatefulWidget { const DictionaryPage({super.key}); @@ -77,10 +75,8 @@ class _DictionaryPageState extends State { }, ]; int _selectedIndex = -1; - // bool _selectedWord = false; @override void initState() { - // TODO: implement initState super.initState(); controller = Get.put(DictionaryController()..getStats()); } @@ -95,7 +91,6 @@ class _DictionaryPageState extends State { backgroundColor: Palette.background, appBar: CustomAppBar( title: "용어사전", - rightIcon: true, onTapTitle: () { showCategoryModal(context); }, @@ -155,7 +150,6 @@ class _DictionaryPageState extends State { child: Container( height: 30, width: 45, - padding: const EdgeInsets.symmetric(horizontal: 16), decoration: BoxDecoration( color: _selectedIndex == index ? const Color(0xFF1EB692) // 선택된 항목은 초록색 @@ -167,19 +161,21 @@ class _DictionaryPageState extends State { : const Color(0xFF1EB692), // 나머지 항목은 회색 테두리 ), ), - child: Text( - consonants[index], // 자음 리스트에서 해당 항목을 표시 - style: _selectedIndex == index - ? const TextStyle( - color: Colors.white, - fontSize: 16, - fontWeight: FontWeight.w500, - ) - : const TextStyle( - color: Color(0xFF767676), - fontSize: 16, - fontWeight: FontWeight.w500, - ), + child: Center( + child: Text( + consonants[index], // 자음 리스트에서 해당 항목을 표시 + style: _selectedIndex == index + ? const TextStyle( + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + ) + : const TextStyle( + color: Color(0xFF767676), + fontSize: 16, + fontWeight: FontWeight.w500, + ), + ), ), ), ), @@ -202,20 +198,11 @@ class _DictionaryPageState extends State { padding: const EdgeInsets.all(16), decoration: const BoxDecoration( color: Colors.white, - // borderRadius: BorderRadius.circular(10), - // border: Border.all(color: const Color(0xFFCFCFCF)), - // boxShadow: [ - // BoxShadow( - // color: Colors.grey.withOpacity(0.1), - // blurRadius: 5, - // offset: const Offset(0, 2), - // ), - // ], ), child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox( - width: 300, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -242,11 +229,8 @@ class _DictionaryPageState extends State { ], ), ), - // const SizedBox( - // width: 5, - // ), Padding( - padding: const EdgeInsets.only(top: 0), + padding: const EdgeInsets.only(left: 10), child: GestureDetector( onTap: () { setState(() { @@ -257,11 +241,16 @@ class _DictionaryPageState extends State { print("Dd"); }); }, - child: Image.asset( - items[index]['selectedWord'] ?? false - ? "assets/bookmark_selected.png" - : "assets/bookmark.png", - height: 20, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 11, vertical: 8), + child: Image.asset( + items[index]['selectedWord'] ?? false + ? "assets/bookmark_selected.png" + : "assets/bookmark.png", + width: 13, + height: 18.2, + ), ), ), ) diff --git a/economic_fe/lib/view/screens/home_page.dart b/economic_fe/lib/view/screens/home_page.dart index 04e039a..1eebd77 100644 --- a/economic_fe/lib/view/screens/home_page.dart +++ b/economic_fe/lib/view/screens/home_page.dart @@ -63,8 +63,6 @@ class _HomePageState extends State { child: Container( width: 317, height: 292, - padding: const EdgeInsets.symmetric( - horizontal: 24, vertical: 20), decoration: ShapeDecoration( color: Colors.white, shape: RoundedRectangleBorder( @@ -73,225 +71,331 @@ class _HomePageState extends State { borderRadius: BorderRadius.circular(12), ), ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, + child: Stack( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - // 진도율 - const Row( - children: [ - Text( - '진도율', - textAlign: TextAlign.center, - style: TextStyle( - color: Color(0xFF111111), - fontSize: 14, - fontWeight: FontWeight.w500, - letterSpacing: -0.35, - ), - ), - Padding( - padding: EdgeInsets.symmetric(horizontal: 2), - child: Icon( - Icons.help_outline, - size: 14, - color: Color(0xffa2a2a2), - ), - ), - ], - ), - // 전체 세트 보기 버튼 - HomeSmallButton( - controller: controller, - onTap: () { - controller.navigateToLearningList(); - }, - text: '전체 세트 보기', - ), - ], - ), - // 진도율 그래프 - SizedBox( - width: 240, - height: 156, - child: Row( + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 24, vertical: 20), + child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - // Beginner - SizedBox( - width: 72, - height: 156, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SizedBox( - width: 44, - child: Text( - '${(beginnerProgress * 100).toInt()}%', + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + // 진도율 + const Row( + children: [ + Text( + '진도율', textAlign: TextAlign.center, - style: const TextStyle( + style: TextStyle( color: Color(0xFF111111), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.50, - letterSpacing: -0.30, + fontSize: 14, + fontWeight: FontWeight.w500, + letterSpacing: -0.35, ), ), - ), - Container( - width: 36, - height: maxHeight * beginnerProgress, - decoration: const ShapeDecoration( - color: Color(0xFFB1F2F2), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(4), - topRight: Radius.circular(4), - ), + Padding( + padding: + EdgeInsets.symmetric(horizontal: 2), + child: Icon( + Icons.help_outline, + size: 14, + color: Color(0xffa2a2a2), ), ), - ), - const Text( - 'Beginner', - style: TextStyle( - color: Color(0xFF404040), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.50, - letterSpacing: -0.30, - ), - ), - ], - ), + ], + ), + // 전체 세트 보기 버튼 + HomeSmallButton( + controller: controller, + onTap: () { + controller.navigateToLearningList(); + }, + text: '전체 세트 보기', + ), + ], ), - // Intermediate + // 진도율 그래프 SizedBox( - width: 72, + width: 240, height: 156, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, + child: Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, children: [ + // Beginner SizedBox( - width: 44, - child: Text( - '${(intermediateProgress * 100).toInt()}%', - textAlign: TextAlign.center, - style: const TextStyle( - color: Color(0xFF111111), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.50, - letterSpacing: -0.30, - ), + width: 72, + height: 156, + child: Column( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + SizedBox( + width: 44, + child: Text( + '${(beginnerProgress * 100).toInt()}%', + textAlign: TextAlign.center, + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, + ), + ), + ), + Container( + width: 36, + height: + maxHeight * beginnerProgress, + decoration: const ShapeDecoration( + color: Color(0xFFB1F2F2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(4), + topRight: Radius.circular(4), + ), + ), + ), + ), + const Text( + 'Beginner', + style: TextStyle( + color: Color(0xFF404040), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, + ), + ), + ], ), ), - Container( - width: 36, - height: maxHeight * intermediateProgress, - decoration: const ShapeDecoration( - color: Color(0xFFB1F2F2), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(4), - topRight: Radius.circular(4), + // Intermediate + SizedBox( + width: 72, + height: 156, + child: Column( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + SizedBox( + width: 44, + child: Text( + '${(intermediateProgress * 100).toInt()}%', + textAlign: TextAlign.center, + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, + ), + ), ), - ), + Container( + width: 36, + height: maxHeight * + intermediateProgress, + decoration: const ShapeDecoration( + color: Color(0xFFB1F2F2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(4), + topRight: Radius.circular(4), + ), + ), + ), + ), + const Text( + 'Intermediate', + style: TextStyle( + color: Color(0xFF404040), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, + ), + ), + ], ), ), - const Text( - 'Intermediate', - style: TextStyle( - color: Color(0xFF404040), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.50, - letterSpacing: -0.30, + // Advanced + SizedBox( + width: 72, + height: 156, + child: Column( + mainAxisAlignment: + MainAxisAlignment.end, + children: [ + SizedBox( + width: 44, + child: Text( + '${(advancedProgress * 100).toInt()}%', + textAlign: TextAlign.center, + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, + ), + ), + ), + Container( + width: 36, + height: + maxHeight * advancedProgress, + decoration: const ShapeDecoration( + color: Color(0xFFB1F2F2), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(4), + topRight: Radius.circular(4), + ), + ), + ), + ), + const Text( + 'Advanced', + style: TextStyle( + color: Color(0xFF404040), + fontSize: 12, + fontWeight: FontWeight.w400, + height: 1.50, + letterSpacing: -0.30, + ), + ), + ], ), ), ], ), ), - // Advanced - SizedBox( - width: 72, - height: 156, - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SizedBox( - width: 44, - child: Text( - '${(advancedProgress * 100).toInt()}%', - textAlign: TextAlign.center, - style: const TextStyle( - color: Color(0xFF111111), - fontSize: 12, - fontWeight: FontWeight.w400, - height: 1.50, - letterSpacing: -0.30, - ), - ), - ), - Container( - width: 36, - height: maxHeight * advancedProgress, - decoration: const ShapeDecoration( - color: Color(0xFFB1F2F2), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(4), - topRight: Radius.circular(4), - ), - ), - ), - ), - const Text( - 'Advanced', + Container( + width: 269, + height: 44, + decoration: ShapeDecoration( + color: const Color(0xFF2AD6D6), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + ), + ), + // 학습 하러 가기 버튼 + child: GestureDetector( + onTap: () { + // 학습 진행 상황에 따라 다음 화면 반환하는 로직 필요 + controller.navigateToLearningList(); + }, + child: const Center( + child: Text( + '학습 하러 가기', style: TextStyle( - color: Color(0xFF404040), - fontSize: 12, - fontWeight: FontWeight.w400, + color: Colors.white, + fontSize: 18, + fontWeight: FontWeight.w600, height: 1.50, - letterSpacing: -0.30, + letterSpacing: -0.45, ), ), - ], + ), ), ), ], ), ), - Container( - width: 269, - height: 44, - decoration: ShapeDecoration( - color: const Color(0xFF2AD6D6), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - ), - ), - // 학습 하러 가기 버튼 - child: GestureDetector( - onTap: () { - // 학습 진행 상황에 따라 다음 화면 반환하는 로직 필요 - controller.navigateToLearningList(); - }, - child: const Center( - child: Text( - '학습 하러 가기', - style: TextStyle( - color: Colors.white, - fontSize: 18, - fontWeight: FontWeight.w600, - height: 1.50, - letterSpacing: -0.45, - ), - ), - ), - ), - ), + Obx(() { + return controller.isProgressContainerVisible.value + ? Container( + width: 337, + height: 292, + decoration: ShapeDecoration( + color: const Color(0xBF111111), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Stack( + children: [ + // 창 닫기 버튼 + Positioned( + right: 0, + child: GestureDetector( + onTap: () => controller + .isProgressContainerVisible + .value = false, + child: const Icon( + Icons.close, + color: Colors.white, + ), + ), + ), + Column( + mainAxisAlignment: + MainAxisAlignment.center, + children: [ + const Center( + child: Text( + '레벨테스트를 진행해야 \n더 정확한 학습이 가능해요.', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.w600, + height: 1.30, + letterSpacing: -0.50, + ), + ), + ), + const SizedBox( + height: 30, + ), + GestureDetector( + onTap: () => + controller.toLevelTest(), + child: Center( + child: Container( + width: 280, + height: 60, + decoration: ShapeDecoration( + color: + Palette.buttonColorBlue, + shape: + RoundedRectangleBorder( + borderRadius: + BorderRadius.circular( + 16), + ), + ), + child: const Center( + child: Text( + '레벨테스트 시작하기', + style: TextStyle( + color: Colors.white, + fontSize: 18, + fontWeight: + FontWeight.w600, + height: 1.20, + letterSpacing: -0.45, + ), + ), + ), + ), + ), + ), + ], + ), + ], + ), + ), + ) + : const SizedBox(); + }), ], ), ), diff --git a/economic_fe/lib/view/screens/learning_set/learning_concept_page.dart b/economic_fe/lib/view/screens/learning_set/learning_concept_page.dart index 2be2daa..ff3a52e 100644 --- a/economic_fe/lib/view/screens/learning_set/learning_concept_page.dart +++ b/economic_fe/lib/view/screens/learning_set/learning_concept_page.dart @@ -1,4 +1,5 @@ import 'package:economic_fe/view/theme/palette.dart'; +import 'package:economic_fe/view/widgets/chatbot_fab.dart'; import 'package:economic_fe/view/widgets/custom_app_bar.dart'; import 'package:economic_fe/view/widgets/stop_option_modal.dart'; import 'package:economic_fe/view_model/learning_set/learning_concept_controller.dart'; @@ -305,6 +306,13 @@ class _LearningConceptPageState extends State { ), ), ), + Positioned( + bottom: 110, + right: 10, + child: ChatbotFAB( + onTap: () => controller.toChatbot(), + ), + ), Positioned( bottom: 0, left: 0, diff --git a/economic_fe/lib/view/screens/learning_set/learning_list_page.dart b/economic_fe/lib/view/screens/learning_set/learning_list_page.dart index b3fc081..78026d0 100644 --- a/economic_fe/lib/view/screens/learning_set/learning_list_page.dart +++ b/economic_fe/lib/view/screens/learning_set/learning_list_page.dart @@ -1,5 +1,6 @@ import 'package:economic_fe/utils/screen_utils.dart'; import 'package:economic_fe/view/theme/palette.dart'; +import 'package:economic_fe/view/widgets/chatbot_fab.dart'; import 'package:economic_fe/view/widgets/custom_app_bar.dart'; import 'package:economic_fe/view/widgets/learning_set/learning_list_item.dart'; import 'package:economic_fe/view_model/learning_set/learning_list_controller.dart'; @@ -39,21 +40,6 @@ class _LearningListPageState extends State { '세금', '경제 지표' ]; - // Map> learningState = { - // 0: [false, false], - // 1: [false, false], - // 2: [false, false], - // 3: [false, false], - // 4: [false, false], - // 5: [false, false], - // 6: [false, false], - // 7: [false, false], - // 8: [false, false], - // 9: [false, false], - // 10: [false, false], - // }; - // GetX 컨트롤러 호출 - // final controller = Get.put(LearningListController()); return Scaffold( backgroundColor: Palette.background, @@ -64,6 +50,9 @@ class _LearningListPageState extends State { controller.navigateToHome(context); }, ), + floatingActionButton: ChatbotFAB( + onTap: () => controller.toChatbot(), + ), body: ListView.builder( itemCount: setTitles.length, // 학습 세트 개수 itemBuilder: (context, index) { diff --git a/economic_fe/lib/view/screens/onboarding/onboarding_card_page.dart b/economic_fe/lib/view/screens/onboarding/onboarding_card_page.dart index a80aa25..78ef875 100644 --- a/economic_fe/lib/view/screens/onboarding/onboarding_card_page.dart +++ b/economic_fe/lib/view/screens/onboarding/onboarding_card_page.dart @@ -1,14 +1,10 @@ -import 'package:economic_fe/data/models/user_model.dart'; import 'package:economic_fe/utils/screen_utils.dart'; import 'package:economic_fe/view/theme/palette.dart'; import 'package:economic_fe/view/widgets/custom_button.dart'; import 'package:economic_fe/view/widgets/onboarding_slide.dart'; import 'package:economic_fe/view_model/onboarding_card_controller.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; class OnboardingCardPage extends StatefulWidget { const OnboardingCardPage({super.key}); diff --git a/economic_fe/lib/view/screens/profile_setting/profile_setting_page.dart b/economic_fe/lib/view/screens/profile_setting/profile_setting_page.dart index f33bf68..6dc54c5 100644 --- a/economic_fe/lib/view/screens/profile_setting/profile_setting_page.dart +++ b/economic_fe/lib/view/screens/profile_setting/profile_setting_page.dart @@ -84,8 +84,10 @@ class ProfileSettingPage extends StatelessWidget { ? Center( child: CustomButton( text: '저장하기', - onPress: () { - controller.toHomePage(); + onPress: () async { + const authToken = + "Bearer eyJhbGciOiJIUz..."; // 실제 Authorization 키 + await controller.saveUserProfile(authToken); }, bgColor: Palette.buttonColorBlue, ), diff --git a/economic_fe/lib/view/screens/quiz/level_select_page.dart b/economic_fe/lib/view/screens/quiz/level_select_page.dart index 116c130..d8b5d21 100644 --- a/economic_fe/lib/view/screens/quiz/level_select_page.dart +++ b/economic_fe/lib/view/screens/quiz/level_select_page.dart @@ -34,7 +34,7 @@ class _LevelSelectPageState extends State { icon: Icons.arrow_back_ios_new, ), body: Padding( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.symmetric(horizontal: 32), child: Center( child: Column( children: [ @@ -42,7 +42,7 @@ class _LevelSelectPageState extends State { height: 62, ), _buildLevelButton( - label: 'Beginner(초급)', + label: '초급', isSelected: _selectedLevel == 'Beginner', onTap: () { setState(() { @@ -53,10 +53,11 @@ class _LevelSelectPageState extends State { // print(controller.selectedLevel); }); }, + isCompleted: true, ), const SizedBox(height: 16), _buildLevelButton( - label: 'Intermediate(중급)', + label: '중급', isSelected: _selectedLevel == 'Intermediate', onTap: () { setState(() { @@ -66,10 +67,11 @@ class _LevelSelectPageState extends State { controller.clickedQuizBtn(context); }); }, + isCompleted: false, ), const SizedBox(height: 16), _buildLevelButton( - label: 'Advanced(고급)', + label: '고급', isSelected: _selectedLevel == 'Advanced', onTap: () { setState(() { @@ -79,6 +81,7 @@ class _LevelSelectPageState extends State { controller.clickedQuizBtn(context); }); }, + isCompleted: false, ), ], ), @@ -91,28 +94,46 @@ class _LevelSelectPageState extends State { required String label, required bool isSelected, required VoidCallback onTap, + required bool isCompleted, }) { return GestureDetector( onTap: onTap, child: Container( - width: 294, + width: MediaQuery.of(context).size.width - 64, padding: const EdgeInsets.all(24), - decoration: BoxDecoration( - color: isSelected - ? const Color(0xFF1EB692) - : Colors.white, // 선택 여부에 따라 색상 변경 - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.grey), - ), - child: Text( - label, - style: TextStyle( - fontSize: 18, - color: - isSelected ? Colors.white : Colors.black, // 선택 여부에 따라 텍스트 색상 변경 - fontWeight: FontWeight.bold, + decoration: ShapeDecoration( + color: isSelected ? Palette.buttonColorGreen : Colors.white, + shape: RoundedRectangleBorder( + side: BorderSide( + width: 1, + color: isSelected + ? Palette.buttonColorGreen + : const Color(0xFFD9D9D9), + ), + borderRadius: BorderRadius.circular(12), ), ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + label, + style: TextStyle( + color: isSelected ? Colors.white : const Color(0xFF111111), + fontSize: 20, + fontWeight: FontWeight.w500, + height: 1.50, + letterSpacing: -0.50, + ), + ), + Icon( + isCompleted ? Icons.check_circle : Icons.check_circle_outline, + color: isCompleted + ? Palette.buttonColorGreen + : const Color(0xffa2a2a2), + ) + ], + ), ), ); } diff --git a/economic_fe/lib/view/widgets/community/comment_widget.dart b/economic_fe/lib/view/widgets/community/comment_widget.dart index 5b3384c..cbb88ae 100644 --- a/economic_fe/lib/view/widgets/community/comment_widget.dart +++ b/economic_fe/lib/view/widgets/community/comment_widget.dart @@ -1,7 +1,6 @@ import 'package:economic_fe/data/models/community/comment.dart'; import 'package:economic_fe/view/widgets/community/option_dialog.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class CommentWidget extends StatelessWidget { final Comment comment; @@ -167,9 +166,65 @@ class CommentWidget extends StatelessWidget { ); }, onReport: () { - // 신고 기능 - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('댓글 신고 기능 실행')), + // // 신고 기능 + // ScaffoldMessenger.of(context).showSnackBar( + // const SnackBar(content: Text('댓글 신고 기능 실행')), + // ); + _onReport(context: context, content: '댓글이 신고되었습니다.'); + }, + ); + } + + // 신고 완료 메시지 + static void _onReport({ + required BuildContext context, + required String content, + }) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + backgroundColor: Colors.white, + content: Container( + width: 312, + height: 108, + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 12), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + content, + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 16, + fontWeight: FontWeight.w400, + height: 1.40, + letterSpacing: -0.40, + ), + ), + Align( + alignment: Alignment.centerRight, + child: GestureDetector( + onTap: () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }, + child: const Text( + '확인', + style: TextStyle( + color: Color(0xFF2AD6D6), + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1.40, + letterSpacing: -0.40, + ), + ), + ), + ), + ], + ), + ), ); }, ); diff --git a/economic_fe/lib/view/widgets/community/option_dialog.dart b/economic_fe/lib/view/widgets/community/option_dialog.dart index b2c8fce..c8ea870 100644 --- a/economic_fe/lib/view/widgets/community/option_dialog.dart +++ b/economic_fe/lib/view/widgets/community/option_dialog.dart @@ -16,68 +16,162 @@ class OptionsDialog { borderRadius: BorderRadius.vertical(top: Radius.circular(16)), ), builder: (context) { - return Wrap( - children: [ - // 옵션 1: 수정 또는 신고 - ListTile( - onTap: () { - Navigator.of(context).pop(); - if (isAuthor) { - onEdit(); // 수정 콜백 실행 - } else { - onReport(); // 신고 콜백 실행 - } - }, - leading: Icon( - isAuthor ? Icons.edit : Icons.flag, - color: isAuthor ? Colors.blue : Colors.red, - ), - title: Text( - isAuthor - ? '수정' - : isComment - ? '댓글 신고하기' - : '글 신고하기', - style: TextStyle( - color: isAuthor ? Colors.blue : Colors.red, - fontSize: 16, - fontWeight: FontWeight.w500, + return Padding( + padding: const EdgeInsets.all(16), + child: Wrap( + children: [ + // 옵션 1: 수정 또는 신고 + ListTile( + onTap: () { + Navigator.of(context).pop(); // 모달 닫기 + if (isAuthor) { + onEdit(); + } else { + _showConfirmationDialog( + context: + Navigator.of(context, rootNavigator: true).context, + content: isComment ? '댓글을 신고하시겠어요?' : '게시글을 신고하시겠어요?', + onConfirm: () { + onReport(); // 신고 처리 콜백 실행 + }, + isReport: true, + ); + } + }, + title: Text( + isAuthor + ? '수정' + : isComment + ? '댓글 신고하기' + : '글 신고하기', + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1.30, + letterSpacing: -0.60, + ), ), ), - ), - // 옵션 2: 삭제 (작성자일 경우에만) - if (isAuthor) + // 옵션 2: 삭제 (작성자일 경우에만) + if (isAuthor) + ListTile( + onTap: () { + Navigator.of(context).pop(); // 모달 닫기 + _showConfirmationDialog( + context: + Navigator.of(context, rootNavigator: true).context, + content: '글을 삭제하시겠어요?', + onConfirm: onDelete, + isReport: false, + ); + }, + title: const Text( + '삭제', + style: TextStyle( + color: Color(0xFF111111), + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1.30, + letterSpacing: -0.60, + ), + ), + ), + // 옵션 3: 닫기 ListTile( onTap: () { Navigator.of(context).pop(); - onDelete(); // 삭제 콜백 실행 }, - leading: const Icon(Icons.delete, color: Colors.red), title: const Text( - '삭제', + '닫기', style: TextStyle( - color: Colors.red, + color: Color(0xFF111111), fontSize: 16, fontWeight: FontWeight.w500, + height: 1.30, + letterSpacing: -0.60, ), ), ), - // 옵션 3: 닫기 - ListTile( - onTap: () { - Navigator.of(context).pop(); - }, - leading: const Icon(Icons.close, color: Colors.grey), - title: const Text( - '닫기', - style: TextStyle( - color: Colors.grey, - fontSize: 16, - fontWeight: FontWeight.w500, + ], + ), + ); + }, + ); + } + + // 신고 또는 삭제 확인 다이얼로그 + static void _showConfirmationDialog({ + required BuildContext context, + required String content, + required VoidCallback onConfirm, + required bool isReport, + }) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + backgroundColor: Colors.white, + content: Container( + width: 312, + height: 108, + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 12), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + content, + style: const TextStyle( + color: Color(0xFF111111), + fontSize: 16, + fontWeight: FontWeight.w400, + height: 1.40, + letterSpacing: -0.40, + ), ), - ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: const Text( + '취소', + style: TextStyle( + color: Color(0xFF9B9A99), + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1.40, + letterSpacing: -0.40, + ), + ), + ), + const SizedBox( + width: 32, + ), + GestureDetector( + onTap: () { + Navigator.of(context).pop; + onConfirm(); + }, + child: Text( + isReport ? '신고' : '확인', + style: TextStyle( + color: isReport + ? const Color(0xFFFF5468) + : const Color(0xFF2AD6D6), + fontSize: 16, + fontWeight: FontWeight.w600, + height: 1.40, + letterSpacing: -0.40, + ), + ), + ), + ], + ), + ], ), - ], + ), ); }, ); diff --git a/economic_fe/lib/view/widgets/custom_snack_bar.dart b/economic_fe/lib/view/widgets/custom_snack_bar.dart new file mode 100644 index 0000000..f737922 --- /dev/null +++ b/economic_fe/lib/view/widgets/custom_snack_bar.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; + +class CustomSnackBar { + static final List _activeSnackBars = []; // 현재 활성 스낵바들 + + static void show({ + required BuildContext context, + required String message, + }) { + final overlay = Overlay.of(context); + + late final OverlayEntry overlayEntry; // 변수 선언만 먼저 함 + overlayEntry = OverlayEntry( + builder: (context) => _PositionedSnackBar( + message: message, + onDismissed: () { + // 스낵바가 사라질 때 리스트에서 제거 + _activeSnackBars.remove(overlayEntry); + overlayEntry.remove(); + }, + positionOffset: _activeSnackBars.length * 50, // 스낵바 간 간격 + ), + ); + + // 스낵바 추가 및 삽입 + _activeSnackBars.add(overlayEntry); + overlay.insert(overlayEntry); + } +} + +class _PositionedSnackBar extends StatefulWidget { + final String message; + final VoidCallback onDismissed; + final double positionOffset; // 스낵바 위치 조정 + + const _PositionedSnackBar({ + required this.message, + required this.onDismissed, + required this.positionOffset, + super.key, + }); + + @override + State<_PositionedSnackBar> createState() => _PositionedSnackBarState(); +} + +class _PositionedSnackBarState extends State<_PositionedSnackBar> + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _slideAnimation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 300), // 애니메이션 시간 + reverseDuration: const Duration(milliseconds: 300), // 닫힐 때 애니메이션 + vsync: this, + ); + + _slideAnimation = + Tween(begin: -50, end: widget.positionOffset) // 위에서 아래로 이동 + .animate( + CurvedAnimation( + parent: _controller, + curve: Curves.easeOut, + ), + ); + + _controller.forward(); + + // 일정 시간 후 애니메이션 역재생 및 스낵바 닫기 + Future.delayed(const Duration(seconds: 1)).then((_) { + if (mounted) { + _controller.reverse().then((_) { + widget.onDismissed(); + }); + } + }); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _controller, + builder: (context, child) { + return Transform.translate( + offset: Offset(0, _slideAnimation.value), + child: child, + ); + }, + child: Align( + alignment: Alignment.topCenter, + child: IntrinsicWidth( + child: Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), + margin: const EdgeInsets.only(top: 70), + decoration: BoxDecoration( + color: Colors.black, + borderRadius: BorderRadius.circular(10), + ), + child: Text( + widget.message, + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white, + fontSize: 14, + fontWeight: FontWeight.w400, + decoration: TextDecoration.none, + ), + ), + ), + ), + ), + ); + } +} diff --git a/economic_fe/lib/view/widgets/quiz_card.dart b/economic_fe/lib/view/widgets/quiz_card.dart index 897b7da..3196fac 100644 --- a/economic_fe/lib/view/widgets/quiz_card.dart +++ b/economic_fe/lib/view/widgets/quiz_card.dart @@ -1,15 +1,9 @@ -import 'package:economic_fe/utils/screen_utils.dart'; import 'package:economic_fe/view/theme/palette.dart'; import 'package:economic_fe/view/widgets/custom_button.dart'; -import 'package:economic_fe/view/widgets/next_button.dart'; -import 'package:economic_fe/view/widgets/stop_option_modal.dart'; - -// import 'package:economic_fe/view/widgets/next_button.dart'; +import 'package:economic_fe/view/widgets/custom_snack_bar.dart'; import 'package:economic_fe/view_model/quiz_controller.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; import '../../utils/text_utils.dart'; @@ -42,281 +36,16 @@ class QuizCard extends StatefulWidget { } class _QuizCardState extends State { - // int? selectedOption; late final QuizController controller; - //final List? oxOption = [" O", " X"]; @override void initState() { super.initState(); controller = Get.put(QuizController()..getStats()); - // controller.isCorrectAnswer = 0; - // controller.clickCheckBtn = false; } - // void selectOption(int index) { - // setState(() { - // // selectedOption = index; - // controller.selectedNumber.value = index; - // print('선택된 번호 : ${controller.selectedNumber.value}'); - // }); - // } - @override Widget build(BuildContext context) { - // return Column( - // children: [ - // Container( - // width: 328, - // height: widget.option == 0 - // ? ScreenUtils.getHeight(context, 570) - // : ScreenUtils.getHeight(context, 370), - // decoration: BoxDecoration( - // border: Border.all(color: const Color(0xffa2a2a2)), - // borderRadius: BorderRadius.circular(10), - // ), - // child: Column( - // children: [ - // Flexible( - // flex: widget.option == 0 ? 4 : 1, - // child: quizQuestion( - // widget.screenWidth, - // Icons.bookmark, - // 'Q. 다음 중 복리 효과가 경제적\n결과로 나타날 수 있는\n상황으로 적절한 것은?', - // ), - // ), - // Flexible( - // flex: widget.option == 0 ? 7 : 1, - // child: Center( - // child: QuizOptionsContainer( - // widget.screenWidth, widget.screenHeight, widget.option), - // ), - // ), - // ], - // ), - // ), - // const SizedBox( - // height: 5, - // ), - // Stack( - // alignment: Alignment.center, - // children: [ - // Obx(() => NextButton( - // // text: widget.isLast && widget.isQuiz ? "확인" : "다음 문제", - // text: widget.isQuiz - // ? "확인" - // : widget.isLast - // ? "레벨테스트 종료" - // : "다음 문제", - // isEnabled: controller.selectedNumber.value != -1, // 활성화 상태 전달 - // onPressed: () { - // widget.isQuiz && controller.isCorrectAnswer == 1 - // ? showModalBottomSheet( - // backgroundColor: const Color(0xFFE1F6FF), - // context: context, - // shape: const RoundedRectangleBorder( - // // borderRadius: BorderRadius.vertical( - // // top: Radius.circular(20)), - // ), - // builder: (context) { - // return Container( - // padding: const EdgeInsets.all(20), - // height: 183, - // width: 362, - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Row( - // children: [ - // SvgPicture.asset( - // 'assets/check_circle.svg', // SVG 파일 경로 - // height: 32, // 높이 - // width: 32, // 너비 - // color: const Color( - // 0xFF067BD5), // 색상 변경 (선택 사항) - // ), - // const SizedBox( - // width: 11.3, - // ), - // const Text( - // '맞았어요!', - // style: TextStyle( - // fontSize: 24, - // fontWeight: FontWeight.w500, - // height: 1.4, - // ), - // ), - // const SizedBox( - // width: 42, - // ), - // ElevatedButton( - // onPressed: () {}, - // style: ElevatedButton.styleFrom( - // backgroundColor: - // Colors.white, // 배경 색상 - // foregroundColor: - // Colors.black, // 글자 색상 - // padding: const EdgeInsets.symmetric( - // horizontal: 20, - // vertical: 15), // 내부 패딩 - // shape: RoundedRectangleBorder( - // borderRadius: - // BorderRadius.circular( - // 10), // 모서리 둥글기 - // side: const BorderSide( - // color: Colors.black, // 테두리 색상 - // width: 0.5, // 테두리 두께 - // ), - // ), - // // elevation: 5, // 그림자 높이 - // ), - // child: const Text("해설보기"), - // ), - // IconButton( - // onPressed: () {}, - // icon: const Icon( - // Icons.bookmark_border)), - // ], - // ), - // const SizedBox(height: 10), - // // const Text( - // // '여기에 모달의 내용을 입력하세요.', - // // style: TextStyle(fontSize: 16), - // // ), - // const Spacer(), - // // ElevatedButton( - // // onPressed: () { - // // Navigator.pop(context); // 모달 닫기 - // // }, - // // child: const Text('닫기'), - // // ), - // NextButton( - // isEnabled: true, - // onPressed: () {}, - // btnColor: Colors.blue, - // ), - // ], - // ), - // ); - // }, - // ) - // // : null; - // : widget.isQuiz && controller.isCorrectAnswer == 2 - // ? showModalBottomSheet( - // backgroundColor: const Color(0xFFFFF2F1), - // context: context, - // shape: const RoundedRectangleBorder( - // // borderRadius: BorderRadius.vertical( - // // top: Radius.circular(20)), - // ), - // builder: (context) { - // return Container( - // padding: const EdgeInsets.all(20), - // height: 183, - // width: 362, - // child: Column( - // crossAxisAlignment: - // CrossAxisAlignment.start, - // children: [ - // Row( - // children: [ - // SvgPicture.asset( - // 'assets/subtract.svg', // SVG 파일 경로 - // height: 32, // 높이 - // width: 32, // 너비 - // color: const Color( - // 0xFFFF5468), // 색상 변경 (선택 사항) - // ), - // const SizedBox( - // width: 11.3, - // ), - // const Text( - // '아쉬워요', - // style: TextStyle( - // fontSize: 24, - // fontWeight: FontWeight.w500, - // height: 1.4, - // ), - // ), - // const SizedBox( - // width: 45, - // ), - // ElevatedButton( - // onPressed: () {}, - // style: ElevatedButton.styleFrom( - // backgroundColor: - // Colors.white, // 배경 색상 - // foregroundColor: - // Colors.black, // 글자 색상 - // padding: - // const EdgeInsets.symmetric( - // horizontal: 20, - // vertical: 15), // 내부 패딩 - // shape: RoundedRectangleBorder( - // borderRadius: - // BorderRadius.circular( - // 10), // 모서리 둥글기 - // side: const BorderSide( - // color: - // Colors.black, // 테두리 색상 - // width: 0.5, // 테두리 두께 - // ), - // ), - // // elevation: 5, // 그림자 높이 - // ), - // child: const Text("해설보기"), - // ), - // IconButton( - // onPressed: () {}, - // icon: const Icon( - // Icons.bookmark_border)), - // ], - // ), - // const SizedBox(height: 10), - // // const Text( - // // '여기에 모달의 내용을 입력하세요.', - // // style: TextStyle(fontSize: 16), - // // ), - // const Spacer(), - // // ElevatedButton( - // // onPressed: () { - // // Navigator.pop(context); // 모달 닫기 - // // }, - // // child: const Text('닫기'), - // // ), - // NextButton( - // isEnabled: true, - // onPressed: () {}, - // btnColor: Colors.red, - // ), - // ], - // ), - // ); - // }, - // ) - // : null; - // - // print("다음 문제로 이동"); - // if (widget.answer == controller.selectedNumber.value && - // widget.isQuiz) { - // setState(() { - // // 정답인 경우 - // controller.isCorrectAnswer = 1; - // controller.clickCheckBtn = true; - // }); - // } else { - // //답이 틀린 경우 - // setState(() { - // controller.isCorrectAnswer = 2; - // controller.clickCheckBtn = true; - // }); - // } - // }, - // )), - // ], - // ), - // ], - // ); return Stack( children: [ Center( @@ -332,7 +61,7 @@ class _QuizCardState extends State { ), // 질문 부분 Container( - width: 328, + width: MediaQuery.of(context).size.width - 32, padding: const EdgeInsets.only( top: 28, left: 40, @@ -351,7 +80,7 @@ class _QuizCardState extends State { ), ), child: SizedBox( - width: 237, + width: MediaQuery.of(context).size.width - (32 + 80), child: Text( addZeroWidthJoiner( widget.question), // 유틸리티 함수 호출 (한글 단어 단위 줄바꿈) @@ -367,7 +96,7 @@ class _QuizCardState extends State { ), ), Container( - width: 328, + width: MediaQuery.of(context).size.width - 32, padding: const EdgeInsets.symmetric( horizontal: 32, vertical: 24), decoration: const BoxDecoration( @@ -597,6 +326,12 @@ class _QuizCardState extends State { onTap: () { controller.isBookmarked.value = !controller.isBookmarked.value; + CustomSnackBar.show( + context: context, + message: + controller.isBookmarked.value + ? '퀴즈를 스크랩했어요' + : '스크랩을 취소했어요'); }, child: Obx(() { return Image.asset( @@ -710,341 +445,6 @@ class _QuizCardState extends State { ], ); } - - // Container quizQuestion( - // double screenWidth, IconData bookmark, String question) { - // return Container( - // decoration: const BoxDecoration( - // color: Color(0xfff2f3f5), - // borderRadius: BorderRadius.only( - // topLeft: Radius.circular(10), - // topRight: Radius.circular(10), - // ), - // border: Border( - // bottom: BorderSide( - // color: Color(0xffa2a2a2), - // ), - // ), - // ), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.stretch, - // children: [ - // const SizedBox( - // height: 15, - // ), - // const SizedBox( - // height: 20, - // ), - // Center( - // child: SizedBox( - // width: screenWidth * 0.6917, - // child: Text( - // question, - // style: const TextStyle( - // fontSize: 20, - // fontWeight: FontWeight.w500, - // fontFamily: 'Pretendard', - // ), - // ), - // ), - // ), - // ], - // ), - // ); - // } - - // Container QuizOptionsContainer( - // double screenWidth, double screenHeight, int option) { - // return Container( - // decoration: const BoxDecoration( - // color: Colors.white, - // borderRadius: BorderRadius.only( - // bottomLeft: Radius.circular(10), - // bottomRight: Radius.circular(10), - // ), - // ), - // child: option == 0 - // ? Column( - // mainAxisAlignment: MainAxisAlignment.spaceEvenly, - // children: widget.option == 0 - // ? List.generate( - // 4, - // (index) => GestureDetector( - // onTap: () { - // selectOption(index + 1); - // print("ddd"); - // if (widget.answer == - // controller.selectedNumber.value) { - // controller.isCorrectAnswer = 1; - // print(controller.isCorrectAnswer); - // } else { - // controller.isCorrectAnswer = 0; - // print(controller.isCorrectAnswer); - // } - // }, - // child: quizOptionCard( - // screenWidth, - // screenHeight, - // controller.selectedNumber.value == index + 1 - // ? const Color(0xff1eb692) - // : Colors.white, - // controller.selectedNumber.value == index + 1 - // ? const Color(0xff1eb692) - // : const Color(0xffd3d3d3), - // index + 1, - // widget.answerOptions![index], - // widget.option, - // ), - // ), - // ) - // : List.generate( - // 2, - // (index) => GestureDetector( - // onTap: () => selectOption(index + 1), - // child: quizOptionCard( - // screenWidth, - // screenHeight, - // controller.selectedNumber.value == index + 1 - // ? const Color(0xff1eb692) - // : Colors.white, - // controller.selectedNumber.value == index + 1 - // ? const Color(0xff1eb692) - // : const Color(0xffd3d3d3), - // index + 1, - // oxOption![index], - // widget.option), - // ), - // ), - // ) - // : Row( - // mainAxisAlignment: MainAxisAlignment.spaceEvenly, - // children: widget.option == 0 - // ? List.generate( - // 4, - // (index) => GestureDetector( - // onTap: () { - // selectOption(index + 1); - // // print("tj"); - // }, - // child: quizOptionCard( - // screenWidth, - // screenHeight, - // controller.selectedNumber.value == index + 1 - // ? const Color(0xff1eb692) - // : Colors.white, - // controller.selectedNumber.value == index + 1 - // ? const Color(0xff1eb692) - // : const Color(0xffd3d3d3), - // index + 1, - // widget.answerOptions![index], - // widget.option), - // ), - // ) - // : List.generate( - // 2, - // (index) => GestureDetector( - // onTap: () => selectOption(index + 1), - // child: quizOptionCard( - // 138, - // 138, - // controller.selectedNumber.value == index + 1 - // ? const Color(0xff1eb692) - // : Colors.white, - // controller.selectedNumber.value == index + 1 - // ? const Color(0xff1eb692) - // : const Color(0xffd3d3d3), - // index + 1, - // oxOption![index], - // widget.option, - // ), - // ), - // ), - // ), - // ); - // } - -// Container quizOptionCard( -// double screenWidth, -// double screenHeight, -// Color background, -// Color border, -// int number, -// String text, -// int option, -// // Color selectedColor, -// ) { -// return Container( -// width: option == 0 ? screenWidth * 0.75 : 138, -// height: option == 0 ? screenHeight * 0.08 : 138, -// decoration: BoxDecoration( -// color: Colors.white, -// border: Border.all( -// color: border, -// width: controller.selectedNumber.value == number ? 3.0 : 1.0, -// ), -// borderRadius: BorderRadius.circular(12), -// ), -// child: Row( -// children: [ -// Padding( -// padding: const EdgeInsets.all(10), -// child: option == 0 -// ? Container( -// width: screenWidth * 0.02, -// height: screenHeight * 0.02, -// decoration: BoxDecoration( -// color: const Color(0xffe5e9ec), -// borderRadius: BorderRadius.circular(5), -// ), -// child: Center( -// child: Text( -// number.toString(), -// style: const TextStyle( -// fontSize: 12, -// fontWeight: FontWeight.w500, -// ), -// ), -// ), -// ) -// : null, -// ), -// const SizedBox( -// width: 10, -// ), -// Text( -// text, -// style: TextStyle( -// fontSize: option == 0 ? 15 : 60, -// fontFamily: 'Pretandard', -// fontWeight: FontWeight.w500, -// height: 1.1, -// color: controller.selectedNumber.value == number -// ? Colors.black -// : Colors.black, -// ), -// ) -// ], -// ), -// ); -// } -// } - // Container quizOptionCard( - // double screenWidth, - // double screenHeight, - // Color background, - // Color border, - // int number, - // String text, - // int option, - // ) { - // bool isSelected = controller.selectedNumber.value == number; - // bool isCorrect = widget.answer == number; // 정답 여부 확인 - - // return Container( - // width: option == 0 ? screenWidth * 0.75 : 138, - // height: option == 0 ? screenHeight * 0.08 : 138, - // decoration: BoxDecoration( - // color: controller.isCorrectAnswer == 1 && - // isSelected && - // controller.clickCheckBtn - // ? const Color(0xffd8eafd) - // : controller.isCorrectAnswer == 2 && - // isSelected && - // controller.clickCheckBtn - // ? const Color(0xFFFFF2F1) - // : Colors.white, - // border: Border.all( - // // color: isCorrect && isSelected - // color: controller.isCorrectAnswer == 1 && - // isSelected && - // controller.clickCheckBtn - // ? const Color(0xff0044cc) // 정답인 경우 파란색 테두리 0xff1eb692 - // : isSelected && - // controller.clickCheckBtn && - // controller.isCorrectAnswer == 2 - // ? const Color.fromARGB(255, 252, 38, 38) - // // : const Color(0xffd3d3d3), - // : isSelected && controller.clickCheckBtn != true //0xffd3d3d3 - // ? const Color(0xff1eb692) - // : const Color(0xffd3d3d3), - // width: isSelected ? 3.0 : 1.0, - // ), - // borderRadius: BorderRadius.circular(12), - // ), - // child: Row( - // children: [ - // Padding( - // padding: const EdgeInsets.all(10), - // child: option == 0 - // ? Container( - // width: screenWidth * 0.02, - // height: screenHeight * 0.02, - // decoration: BoxDecoration( - // color: const Color(0xffe5e9ec), - // borderRadius: BorderRadius.circular(5), - // ), - // child: Center( - // child: Text( - // number.toString(), - // style: const TextStyle( - // fontSize: 12, - // fontWeight: FontWeight.w500, - // ), - // ), - // ), - // ) - // : null, - // ), - // const SizedBox( - // width: 10, - // ), - // Text( - // text, - // style: TextStyle( - // fontSize: option == 0 ? 15 : 60, - // fontFamily: 'Pretendard', - // fontWeight: FontWeight.w500, - // height: 1.1, - // color: isCorrect && isSelected && controller.clickCheckBtn - // ? const Color(0xff0044cc) // 파란색 글자 - // : Colors.black, // 기본 검정색 - // ), - // ) - // ], - // ), - // ); - // } - -// class NextButton extends StatelessWidget { -// const NextButton({ -// super.key, -// }); - -// @override -// Widget build(BuildContext context) { -// return ElevatedButton( -// onPressed: () => {print("onpressed")}, -// style: ElevatedButton.styleFrom( -// minimumSize: Size( -// ScreenUtils.getWidth(context, 328), -// ScreenUtils.getHeight(context, 56), -// ), -// backgroundColor: const Color(0x001eb692), -// shape: RoundedRectangleBorder( -// borderRadius: BorderRadius.circular(16), // border-radius: 16px -// ), -// alignment: Alignment.center, // 텍스트 정렬 -// padding: EdgeInsets.zero, -// ), -// child: const Text("다음 문제", -// style: TextStyle( -// color: Color(0xFF111111), -// fontSize: 20, -// fontWeight: FontWeight.w500, -// )), -// ); -// } -// } } // OX 문제 선택지 박스 @@ -1134,7 +534,7 @@ class MultipleOptionContainer extends StatelessWidget { @override Widget build(BuildContext context) { return Container( - width: 264, + width: MediaQuery.of(context).size.width - (64 + 32), padding: const EdgeInsets.all(16), decoration: isSelected ? isQuiz @@ -1169,7 +569,6 @@ class MultipleOptionContainer extends StatelessWidget { ), ), child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( width: 24, @@ -1202,16 +601,18 @@ class MultipleOptionContainer extends StatelessWidget { ), ), ), - SizedBox( - width: 190, + const SizedBox( + width: 12, + ), + Expanded( child: Text( addZeroWidthJoiner(widget.answerOptions![optionNum - 1]), style: const TextStyle( color: Color(0xFF111111), - fontSize: 16, - fontFamily: 'Pretendard Variable', + fontSize: 18, fontWeight: FontWeight.w500, - height: 1.4, + height: 1.40, + letterSpacing: -0.45, ), ), ), diff --git a/economic_fe/lib/view_model/home_controller.dart b/economic_fe/lib/view_model/home_controller.dart index 1531247..84a5a09 100644 --- a/economic_fe/lib/view_model/home_controller.dart +++ b/economic_fe/lib/view_model/home_controller.dart @@ -1,6 +1,14 @@ import 'package:get/get.dart'; // GoRouter import class HomeController extends GetxController { + // 진도율 가시성 관리 (레벨테스트 진행 여부에 따른 로직으로 수정 필요) + var isProgressContainerVisible = true.obs; + + // 레벨테스트 시작 화면으로 + void toLevelTest() { + Get.toNamed('/test'); + } + // 전체 학습 세트 목록 화면으로 전환 void navigateToLearningList() { Get.offNamed('/learning_list'); diff --git a/economic_fe/lib/view_model/learning_set/learning_concept_controller.dart b/economic_fe/lib/view_model/learning_set/learning_concept_controller.dart index 3944ad5..0fb0277 100644 --- a/economic_fe/lib/view_model/learning_set/learning_concept_controller.dart +++ b/economic_fe/lib/view_model/learning_set/learning_concept_controller.dart @@ -13,6 +13,11 @@ class LearningConceptController extends GetxController { Get.to(() => LearningConceptPage(currentStep: currentStepIdx)); } + // 챗봇 화면으로 이동 + void toChatbot() { + Get.toNamed('/chatbot'); + } + // 학습 완료 창으로 이동 void clickedFinishBtn(BuildContext context) { // Get.to(() => const LearningListPage()); diff --git a/economic_fe/lib/view_model/learning_set/learning_list_controller.dart b/economic_fe/lib/view_model/learning_set/learning_list_controller.dart index 66f3010..6cd80e9 100644 --- a/economic_fe/lib/view_model/learning_set/learning_list_controller.dart +++ b/economic_fe/lib/view_model/learning_set/learning_list_controller.dart @@ -39,6 +39,11 @@ class LearningListController extends GetxController { Get.offNamed('/learning_list/learning_concept'); } + // 챗봇 화면으로 전환 + void toChatbot() { + Get.toNamed('/chatbot'); + } + Future getLearningConcept(int learningSetId, String level) async { try { print("start"); diff --git a/economic_fe/lib/view_model/profile_setting/basic_controller.dart b/economic_fe/lib/view_model/profile_setting/basic_controller.dart index bb2ec1b..5bba981 100644 --- a/economic_fe/lib/view_model/profile_setting/basic_controller.dart +++ b/economic_fe/lib/view_model/profile_setting/basic_controller.dart @@ -47,32 +47,76 @@ class BasicController extends GetxController { await showDialog( context: context, builder: (context) { - return AlertDialog( - title: const Text('프로필 사진 선택'), - actions: [ - TextButton( - onPressed: () async { - Navigator.pop(context); - var image = await _imagePickerService.pickImageFromGallery(); - if (image != null) { - selectedProfileImage.value = image.path; // 이미지 경로 저장 - print('Selected image path: ${image.path}'); - } - }, - child: const Text('갤러리'), + return Dialog( + backgroundColor: Colors.white, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 25, horizontal: 27), + child: Column( + mainAxisSize: MainAxisSize.min, // 다이얼로그 크기를 내용에 맞게 조정 + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + '프로필 사진', + style: TextStyle( + color: Color(0xFF111111), + fontSize: 24, + fontWeight: FontWeight.w600, + height: 1.30, + letterSpacing: -0.60, + ), + ), + const SizedBox( + height: 25.5, + ), + // 사진첩 버튼 + GestureDetector( + onTap: () async { + Navigator.pop(context); + var image = + await _imagePickerService.pickImageFromGallery(); + if (image != null) { + selectedProfileImage.value = image.path; // 이미지 경로 저장 + print('Selected image path: ${image.path}'); + } + }, + child: const Text( + '사진첩', + style: TextStyle( + color: Color(0xFF111111), + fontSize: 20, + fontWeight: FontWeight.w400, + height: 1.30, + letterSpacing: -0.50, + ), + ), + ), + const SizedBox( + height: 28, + ), + // 카메라 버튼 + GestureDetector( + onTap: () async { + Navigator.pop(context); + var image = await _imagePickerService.pickImageFromCamera(); + if (image != null) { + selectedProfileImage.value = image.path; // 이미지 경로 저장 + print('Captured image path: ${image.path}'); + } + }, + child: const Text( + '카메라', + style: TextStyle( + color: Color(0xFF111111), + fontSize: 20, + fontWeight: FontWeight.w400, + height: 1.30, + letterSpacing: -0.50, + ), + ), + ), + ], ), - TextButton( - onPressed: () async { - Navigator.pop(context); - var image = await _imagePickerService.pickImageFromCamera(); - if (image != null) { - selectedProfileImage.value = image.path; // 이미지 경로 저장 - print('Captured image path: ${image.path}'); - } - }, - child: const Text('카메라'), - ), - ], + ), ); }, ); @@ -137,7 +181,13 @@ class BasicController extends GetxController { // 저장하기 버튼 클릭 상태 업데이트 void onSaveButtonClicked() async { - _updateSaveButtonState(); + Get.find().updateBasicInfo( + ageRange: selectedBirthday.value!, + gender: selectedGender.value!, + profileIntro: userInput.value, + ); + Get.find().updateNickname(nickname.value); + saveButtonClicked.value = true; // 상태 변경 Get.find().updateBasicSaveButtonClicked(); } } diff --git a/economic_fe/lib/view_model/profile_setting/job_select_controller.dart b/economic_fe/lib/view_model/profile_setting/job_select_controller.dart index 573d95e..92a7dd1 100644 --- a/economic_fe/lib/view_model/profile_setting/job_select_controller.dart +++ b/economic_fe/lib/view_model/profile_setting/job_select_controller.dart @@ -29,8 +29,8 @@ class JobSelectController extends GetxController { // 저장하기 버튼 클릭 상태 업데이트 void onSaveButtonClicked() { - saveButtonClicked.value = true; // 버튼 클릭 시 상태 변경 - // ProfileSettingController에 있는 메서드 호출 + Get.find().updateBusinessType(selectedJob.value!); + saveButtonClicked.value = true; // 상태 변경 Get.find().updateJobSaveButtonClicked(); } } diff --git a/economic_fe/lib/view_model/profile_setting/part_select_controller.dart b/economic_fe/lib/view_model/profile_setting/part_select_controller.dart index b75218c..55b1f4e 100644 --- a/economic_fe/lib/view_model/profile_setting/part_select_controller.dart +++ b/economic_fe/lib/view_model/profile_setting/part_select_controller.dart @@ -29,7 +29,8 @@ class PartSelectController extends GetxController { // 저장하기 버튼 클릭 상태 업데이트 void onSaveButtonClicked() { - saveButtonClicked.value = true; // 버튼 클릭 시 상태 변경 + Get.find().updateJob(selectedPart.value!); + saveButtonClicked.value = true; // 상태 변경 Get.find().updatePartSaveButtonClicked(); } } diff --git a/economic_fe/lib/view_model/profile_setting/profile_setting_controller.dart b/economic_fe/lib/view_model/profile_setting/profile_setting_controller.dart index 7f2e0d0..21a47aa 100644 --- a/economic_fe/lib/view_model/profile_setting/profile_setting_controller.dart +++ b/economic_fe/lib/view_model/profile_setting/profile_setting_controller.dart @@ -1,5 +1,7 @@ import 'dart:developer'; +import 'package:economic_fe/data/models/user_profile.dart'; +import 'package:economic_fe/data/services/remote_data_source.dart'; import 'package:economic_fe/view_model/profile_setting/basic_controller.dart'; import 'package:economic_fe/view_model/profile_setting/job_select_controller.dart'; import 'package:economic_fe/view_model/profile_setting/part_select_controller.dart'; @@ -60,4 +62,65 @@ class ProfileSettingController extends GetxController { void toHomePage() { Get.toNamed('/home'); } + + // 사용자 프로필 정보 통합 관리 + final Rx userProfile = UserProfile( + nickname: '', + businessType: '', + job: '', + ageRange: '', + gender: '', + ).obs; + + void updateNickname(String nickname) { + userProfile.update((profile) { + profile?.nickname = nickname; + }); + } + + void updateBusinessType(String businessType) { + userProfile.update((profile) { + profile?.businessType = businessType; + }); + } + + void updateJob(String job) { + userProfile.update((profile) { + profile?.job = job; + }); + } + + void updateBasicInfo({ + required String ageRange, + required String gender, + String? profileIntro, + }) { + userProfile.update((profile) { + profile?.ageRange = ageRange; + profile?.gender = gender; + profile?.profileIntro = profileIntro; + }); + } + + Future saveUserProfile(String authToken) async { + // 모든 저장 버튼이 클릭되었는지 확인 + if (!basicSaveButtonClicked.value || + !jobSaveButtonClicked.value || + !partSaveButtonClicked.value) { + Get.snackbar('오류', '모든 항목을 완료해주세요.'); + return; + } + + final response = await RemoteDataSource.postApi( + 'api/v1/user/profile', + userProfile.value.toJson(), // Token 전달 + ); + + if (response == 200) { + Get.snackbar('성공', '프로필이 저장되었습니다.'); + toHomePage(); // 홈 화면으로 이동 + } else { + Get.snackbar('오류', '프로필 저장 중 문제가 발생했습니다.'); + } + } } diff --git a/economic_fe/lib/view_model/quiz/level_select_controller.dart b/economic_fe/lib/view_model/quiz/level_select_controller.dart index 1d1294c..211dfa2 100644 --- a/economic_fe/lib/view_model/quiz/level_select_controller.dart +++ b/economic_fe/lib/view_model/quiz/level_select_controller.dart @@ -1,7 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; class LevelSelectController extends GetxController { late BuildContext context; @@ -13,10 +12,10 @@ class LevelSelectController extends GetxController { } void clickedTestBtn(BuildContext context) { - context.go('/test'); + Get.toNamed('/test'); } void clickedQuizBtn(BuildContext context) { - context.go('/learning_list/quiz_level/quiz'); + Get.toNamed('/learning_list/quiz_level/quiz'); } } diff --git a/economic_fe/pubspec.lock b/economic_fe/pubspec.lock index 3ef30e0..a51ef91 100644 --- a/economic_fe/pubspec.lock +++ b/economic_fe/pubspec.lock @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.0" convert: dependency: transitive description: @@ -380,18 +380,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" + sha256: "7bb2830ebd849694d1ec25bf1f44582d6ac531a57a365a803a6034ff751d2d06" url: "https://pub.dev" source: hosted - version: "10.0.5" + version: "10.0.7" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" + sha256: "9491a714cca3667b60b5c420da8217e6de0d1ba7a5ec322fab01758f6998f379" url: "https://pub.dev" source: hosted - version: "3.0.5" + version: "3.0.8" leak_tracker_testing: dependency: transitive description: @@ -580,7 +580,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: @@ -593,10 +593,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -609,10 +609,10 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.3.0" term_glyph: dependency: transitive description: @@ -625,10 +625,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.2" + version: "0.7.3" typed_data: dependency: transitive description: @@ -737,10 +737,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" + sha256: f6be3ed8bd01289b34d679c2b62226f63c0e69f9fd2e50a6b3c1c729a961041b url: "https://pub.dev" source: hosted - version: "14.2.5" + version: "14.3.0" web: dependency: transitive description: