diff --git a/assets/icons/art.png b/assets/icons/art.png new file mode 100644 index 0000000..491181e Binary files /dev/null and b/assets/icons/art.png differ diff --git a/assets/icons/book.png b/assets/icons/book.png new file mode 100644 index 0000000..83810e3 Binary files /dev/null and b/assets/icons/book.png differ diff --git a/assets/icons/chat.png b/assets/icons/chat.png new file mode 100644 index 0000000..9a0ba0b Binary files /dev/null and b/assets/icons/chat.png differ diff --git a/assets/icons/etc.png b/assets/icons/etc.png new file mode 100644 index 0000000..0900bd3 Binary files /dev/null and b/assets/icons/etc.png differ diff --git a/assets/icons/guitar.png b/assets/icons/guitar.png new file mode 100644 index 0000000..db6d9b8 Binary files /dev/null and b/assets/icons/guitar.png differ diff --git a/assets/icons/keyboard.png b/assets/icons/keyboard.png new file mode 100644 index 0000000..1015c9b Binary files /dev/null and b/assets/icons/keyboard.png differ diff --git a/assets/icons/login.png b/assets/icons/login.png new file mode 100644 index 0000000..f34caf2 Binary files /dev/null and b/assets/icons/login.png differ diff --git a/assets/icons/oven.png b/assets/icons/oven.png new file mode 100644 index 0000000..61a1094 Binary files /dev/null and b/assets/icons/oven.png differ diff --git a/assets/icons/startup.png b/assets/icons/startup.png new file mode 100644 index 0000000..045a4b5 Binary files /dev/null and b/assets/icons/startup.png differ diff --git a/assets/icons/travel.png b/assets/icons/travel.png new file mode 100644 index 0000000..9578d68 Binary files /dev/null and b/assets/icons/travel.png differ diff --git a/assets/icons/volunteer.png b/assets/icons/volunteer.png new file mode 100644 index 0000000..28abce1 Binary files /dev/null and b/assets/icons/volunteer.png differ diff --git a/assets/images/road_bottom.png b/assets/images/road_bottom.png new file mode 100644 index 0000000..080f3d6 Binary files /dev/null and b/assets/images/road_bottom.png differ diff --git a/assets/images/stage_road.png b/assets/images/stage_road.png new file mode 100644 index 0000000..fbcd42b Binary files /dev/null and b/assets/images/stage_road.png differ diff --git a/lib/core/user_model.dart b/lib/core/user_model.dart index c587b71..35e109f 100644 --- a/lib/core/user_model.dart +++ b/lib/core/user_model.dart @@ -1,5 +1,4 @@ import 'package:componentss/features/study/data/group_model.dart'; -import 'package:componentss/features/study/data/tempGroup.dart'; class User { final String username; diff --git a/lib/core/user_provider.dart b/lib/core/user_provider.dart index d1ab13e..153bbc0 100644 --- a/lib/core/user_provider.dart +++ b/lib/core/user_provider.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:componentss/features/study/data/group_model.dart'; -import 'package:componentss/features/study/data/tempGroup.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'user_model.dart'; diff --git a/lib/core/widgets/customBottomNavigationBar.dart b/lib/core/widgets/customBottomNavigationBar.dart index 5c963d1..c22829f 100644 --- a/lib/core/widgets/customBottomNavigationBar.dart +++ b/lib/core/widgets/customBottomNavigationBar.dart @@ -35,16 +35,16 @@ class _CustomBottomNavigationBarState extends State { BottomNavigationBarItem( icon: Padding( padding: EdgeInsets.only(bottom: 4), - child: Icon(CustomIcon.study), + child: Icon(CustomIcon.baking), ), - label: '스터디', + label: '베이킹', ), BottomNavigationBarItem( icon: Padding( padding: EdgeInsets.only(bottom: 4), - child: Icon(CustomIcon.baking), + child: Icon(CustomIcon.study), ), - label: '베이킹', + label: '스터디', ), BottomNavigationBarItem( icon: Padding( diff --git a/lib/features/auth/login_screen.dart b/lib/features/auth/login_screen.dart index a45fd3b..2201904 100644 --- a/lib/features/auth/login_screen.dart +++ b/lib/features/auth/login_screen.dart @@ -149,7 +149,7 @@ class _LoginScreenState extends State { ), ), Text( - '|', + 'ㅣ', style: TextStyle( color: const Color(0xFF6B6B6B) /* dark-gray */, fontSize: 40.sp, diff --git a/lib/features/auth/onboarding_screen.dart b/lib/features/auth/onboarding_screen.dart index 702a9d5..baa2c12 100644 --- a/lib/features/auth/onboarding_screen.dart +++ b/lib/features/auth/onboarding_screen.dart @@ -11,7 +11,17 @@ class OnboardingScreen extends StatelessWidget { return Scaffold( body: Column( children: [ - SizedBox(height: 1844.h), + SizedBox(height: 250.h), + Positioned( + left: 44.w, + top: 342.h, + child: Image.asset( + 'assets/icons/login.png', + width: 992.w, + height: 1360.h, + ), + ), + SizedBox(height: 200.h), GestureDetector( onTap: () { Navigator.push( diff --git a/lib/features/baking/UI/baking_screen.dart b/lib/features/baking/UI/baking_screen.dart new file mode 100644 index 0000000..ff128a0 --- /dev/null +++ b/lib/features/baking/UI/baking_screen.dart @@ -0,0 +1,640 @@ +import 'dart:math'; + +import 'package:componentss/core/user_provider.dart'; +import 'package:componentss/features/baking/UI/baking_stage.dart'; +import 'package:componentss/features/baking/data/attendacne/attendance_api.dart'; +import 'package:componentss/features/baking/data/attendacne/attendance_model.dart'; +import 'package:componentss/features/baking/data/mission/mission_api.dart'; +import 'package:componentss/features/baking/data/mission/mission_model.dart'; +import 'package:componentss/features/baking/data/mission/mission_response_model.dart'; +import 'package:componentss/features/baking/UI/questions/even/answer_screen.dart'; +import 'package:componentss/features/baking/UI/questions/odd/odd_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:provider/provider.dart'; + +class BakingScreen extends StatefulWidget { + const BakingScreen({super.key}); + + @override + State createState() => _BakingScreenState(); +} + +class _BakingScreenState extends State { + late MissionResponse? _missionResponse; + bool _isLoading = true; // 로딩 상태를 관리 + final List dailyQuests = []; + late List _attendanceHistory; + + @override + void initState() { + super.initState(); + // _loadMissionData(); + _attendanceHistory = []; // 초기화 + _loadMissionAndAttendanceData(); // 미션과 + // 유저 ID를 사용하여 미션 데이터를 가져옵니다. + } + + Future _loadMissionAndAttendanceData() async { + try { + // 미션 데이터와 출석 데이터를 병렬로 가져옴 + final userProvider = Provider.of(context, listen: false); + final user = userProvider.user; + final missionResponseFuture = fetchNextMissions( + user!.username, + ); // 유저 ID를 실제 값으로 대체 + final attendanceHistoryFuture = AttendanceApi().fetchAttendanceHistory( + user.username, + ); + + final results = await Future.wait([ + missionResponseFuture, + attendanceHistoryFuture, + ]); + + final missionResponse = results[0] as MissionResponse; + final attendanceHistory = results[1] as List; + + setState(() { + _missionResponse = missionResponse; + _attendanceHistory = attendanceHistory; + _isLoading = false; + + // 출석 데이터와 미션 데이터를 매칭하여 Quest의 isCompleted 설정 + final attendance = + attendanceHistory.isNotEmpty ? attendanceHistory.last : null; + + dailyQuests.add( + Quest( + title: "모범답안 작성하기", + subtitle: missionResponse.nextOddMission.question, + stage: + "Stage ${missionResponse.nextOddMission.stage}-${missionResponse.nextOddMission.index}", + isCompleted: attendance?.oddMissionCompleted ?? false, + ), + ); + + dailyQuests.add( + Quest( + title: "랜덤질문에 답변 연습하기", + subtitle: + attendance!.evenMissionCompleted + ? missionResponse.nextEvenMission.question + : "", + stage: + "Stage ${missionResponse.nextEvenMission.stage}-${missionResponse.nextEvenMission.index}", + isCompleted: attendance.evenMissionCompleted, + ), + ); + }); + } catch (error) { + print("Error loading mission or attendance data: $error"); + setState(() { + _isLoading = false; + }); + } + } + + Widget _buildAttendanceSection() { + // 요일 이름과 날짜를 매핑 + List days = ['월', '화', '수', '목', '금', '토', '일']; + DateTime startOfWeek = DateTime.now().subtract( + Duration(days: DateTime.now().weekday - 1), + ); + + return Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Container( + width: 1000.w, + height: 300.h, + decoration: BoxDecoration( + border: Border.all(color: const Color(0xFFD9D9D9)), + borderRadius: BorderRadius.circular(38.50.w), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(7, (index) { + // 현재 주의 날짜 계산 + DateTime currentDate = startOfWeek.add(Duration(days: index)); + String formattedDate = + currentDate.toIso8601String().split('T').first; + + // 출석 여부 확인 + bool isAttended = _attendanceHistory.any( + (attendance) => + attendance.attendanceSatisfied && + attendance.date == formattedDate, + ); + + bool isToday = + currentDate.day == DateTime.now().day && + currentDate.month == DateTime.now().month && + currentDate.year == DateTime.now().year; + + return Padding( + padding: const EdgeInsets.only(top: 20), + child: Column( + children: [ + isToday + ? Text( + days[index], // 요일 표시 + style: TextStyle( + color: Colors.black, + fontSize: 36.sp, + fontWeight: FontWeight.w600, + ), + ) + : Text( + days[index], // 요일 표시 + style: TextStyle( + color: Color(0xFF6B6B6B), + fontSize: 36.sp, + fontWeight: FontWeight.w400, + ), + ), + SizedBox(height: 10), + Container( + width: 100.w, + height: 100.h, + margin: EdgeInsets.symmetric(horizontal: 20.w), + decoration: BoxDecoration( + color: + isAttended ? Colors.orange : Colors.grey.shade300, + shape: BoxShape.circle, + ), + child: Center( + child: Icon( + isAttended ? Icons.check : null, + color: Colors.white, + // 출석 여부 표시 + ), + ), + ), + SizedBox(height: 10), + ], + ), + ); + }), + ), + ), + ], + ), + ); + } + + Widget _buildQuestItem(Quest quest, MissionResponse missionresponse) { + return GestureDetector( + onTap: () { + if (quest.title == "모범답안 작성하기") { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => BakingStage()), + ); + } else if (quest.title == "랜덤질문에 답변 연습하기") { + Navigator.push( + context, + MaterialPageRoute(builder: (context) => BakingStage()), + ); + // 다른 퀘스트에 대한 동작 추가 + } + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), + width: 1000.w, + decoration: BoxDecoration( + color: quest.isCompleted ? const Color(0x21FF9F1C) : Colors.white, + borderRadius: BorderRadius.circular(8.0), + border: Border.all( + color: quest.isCompleted ? Colors.orange : Colors.grey.shade300, + ), + ), + + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Quest 객체의 데이터를 사용 + const SizedBox(height: 4), + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), + decoration: BoxDecoration( + color: quest.isCompleted ? Colors.orange : Colors.grey.shade400, + borderRadius: BorderRadius.circular(20), + ), + child: Text( + quest.stage, + style: TextStyle( + fontSize: 10, + color: + quest.isCompleted ? Colors.white : Colors.grey.shade800, + fontWeight: FontWeight.bold, + ), + ), + ), + + const SizedBox(height: 10), + Text( + quest.title, + style: const TextStyle(fontSize: 17, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 3), + Divider( + color: quest.isCompleted ? Colors.orange : Colors.grey.shade400, + thickness: 1.0, + indent: 0.5, + endIndent: 0.5, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + quest.subtitle, + style: const TextStyle(fontSize: 12, color: Colors.black54), + ), + + ElevatedButton( + onPressed: () { + print("object"); + }, + style: ElevatedButton.styleFrom( + backgroundColor: + quest.isCompleted + ? Colors.orange + : Colors.grey.shade800, + + elevation: 0, + padding: EdgeInsets.zero, + minimumSize: const Size(65, 30), + + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(20.0), + ), + visualDensity: VisualDensity.compact, + ), + + child: Text( + quest.isCompleted ? '완료' : '바로가기', + style: TextStyle(fontSize: 10, color: Colors.white), + ), + ), + ], + ), + ], + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + child: Column( + children: [ + Stack( + children: [ + Container( + width: double.infinity, + color: Colors.white, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 100), + Text('logo'), + Center( + child: Container( + width: 1000.w, + height: 200.h, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 2, + ), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + color: Color(0x21FF9F1C), // 주황색 + shape: RoundedRectangleBorder( + side: BorderSide( + width: 2.96.w, + color: const Color( + 0xFFFF9F1C, + ) /* main-orange */, + ), + borderRadius: BorderRadius.circular(29.57.w), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 29.57.w, + children: [ + Text( + '동아리 면접', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.black, + fontSize: 36.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ), + + Row( + children: [ + Container( + width: 130.w, + height: 154.h, + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(35.r), + ), + ), + + Container( + width: 130.w, + height: 154.h, + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(35.r), + ), + ), + + Container( + width: 130.w, + height: 154.h, + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(35.r), + ), + ), + ], + ), + Stack( + children: [ + Center(child: AnimatedHalfCircleProgress()), + Padding( + padding: EdgeInsets.only(top: 90), + child: Center( + child: Container( + width: 611.w, + height: 500.h, + decoration: BoxDecoration( + color: Color(0XFF6B6B6B), + ), + ), + ), + ), + ], + ), + SizedBox(height: 20), + Center( + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 2, + ), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + color: const Color(0x21FF9F1C), + shape: RoundedRectangleBorder( + side: BorderSide( + width: 2.96.w, + color: const Color( + 0xFFFF9F1C, + ) /* main-orange */, + ), + borderRadius: BorderRadius.circular(29.57.w), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 29.57.w, + children: [ + Text( + 'Lv.0', + textAlign: TextAlign.center, + style: TextStyle( + color: const Color( + 0xFFFF9F1C, + ) /* main-orange */, + fontSize: 36.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ), + SizedBox(height: 6), + Center( + child: Text( + '따끈따끈한 반죽', + style: TextStyle( + color: Colors.black /* white */, + fontSize: 66.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + ), + ), + ), + SizedBox(height: 200.h), + + Padding( + padding: EdgeInsets.only(left: 20), + child: Text( + '이번 주 출석 현황', + style: TextStyle( + color: Colors.black /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + ), + ), + ), + const SizedBox(height: 12), + _buildAttendanceSection(), + + SizedBox(height: 40), + Padding( + padding: EdgeInsets.only(left: 20), + child: Text( + '일일 퀘스트', + style: TextStyle( + color: Colors.black /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + ), + ), + ), + SizedBox(height: 20), + Padding( + padding: EdgeInsets.only(left: 20, right: 20), + child: Column( + // map을 사용하여 각 Quest 객체를 _buildQuestItem 위젯으로 변환 + // .toList()로 위젯 리스트 생성 + children: + dailyQuests + .map( + (quest) => Padding( + padding: const EdgeInsets.only( + bottom: 10.0, + ), + child: _buildQuestItem( + quest, + _missionResponse!, + ), + ), + ) + .toList(), + ), + ), + SizedBox(height: 50), + Padding( + padding: EdgeInsets.only(left: 20, right: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + '나만의 모범 답안', + style: TextStyle( + color: Colors.black /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + ), + ), + GestureDetector( + onTap: () { + //Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) {} + }, + child: Row( + children: [ + const Text('전체보기'), + Icon(Icons.arrow_forward_ios, size: 15), + ], + ), + ), + ], + ), + ), + SizedBox(height: 20), + + //Padding( + // padding: EdgeInsets.only(left: 20, right: 20), + // child: Column( + // children: [ + SizedBox(height: 100), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } +} + +class AnimatedHalfCircleProgress extends StatefulWidget { + const AnimatedHalfCircleProgress({super.key}); + + @override + _AnimatedHalfCircleProgressState createState() => + _AnimatedHalfCircleProgressState(); +} + +class _AnimatedHalfCircleProgressState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _animation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: Duration(seconds: 1), + )..repeat(reverse: false); + + _animation = Tween(begin: 0, end: 0.5).animate(_controller); + + _controller.forward(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return CustomPaint( + size: Size(900.w, 450.h), + painter: HalfCircleProgressPainter(progress: _animation.value), + ); + }, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } +} + +class HalfCircleProgressPainter extends CustomPainter { + final double progress; + + HalfCircleProgressPainter({required this.progress}); + + @override + void paint(Canvas canvas, Size size) { + Paint backgroundPaint = + Paint() + ..color = Color(0xFFD9D9D9) + ..strokeWidth = 10 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + Paint progressPaint = + Paint() + ..color = Color(0xFFFF9F1C) + ..strokeWidth = 10 + ..style = PaintingStyle.stroke + ..strokeCap = StrokeCap.round; + + Rect rect = Rect.fromLTWH(0, 0, size.width, size.height * 2); + + canvas.drawArc(rect, pi, pi, false, backgroundPaint); + canvas.drawArc(rect, pi, pi * progress, false, progressPaint); + } + + @override + bool shouldRepaint(CustomPainter oldDelegate) => true; +} + +class Quest { + final String title; + final String subtitle; + final String stage; + final bool isCompleted; + + Quest({ + required this.title, + required this.subtitle, + required this.stage, + required this.isCompleted, + }); +} diff --git a/lib/features/baking/UI/baking_stage.dart b/lib/features/baking/UI/baking_stage.dart new file mode 100644 index 0000000..bde9bfa --- /dev/null +++ b/lib/features/baking/UI/baking_stage.dart @@ -0,0 +1,266 @@ +import 'package:componentss/features/baking/UI/baking_screen.dart'; +import 'package:componentss/features/baking/data/mission/mission_model.dart'; +import 'package:componentss/features/baking/UI/questions/odd/odd_quiz.dart'; +import 'package:componentss/features/main_screen.dart'; +import 'package:componentss/icons/custom_icon_icons.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class BakingStage extends StatefulWidget { + const BakingStage({super.key}); + + @override + State createState() => _BakingStageState(); +} + +class _BakingStageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Container( + width: 1080.w, + height: 2857.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(color: Colors.white), + child: Stack( + children: [ + Positioned( + left: 0.w, + top: -67.h, + child: Container( + width: 1080.w, + height: 3575.h, + decoration: BoxDecoration(color: const Color(0xFF95E08A)), + ), + ), + Positioned( + top: 281.h, + child: Image.asset('assets/images/stage_road.png'), + width: 1080.w, + height: 2000.h, + ), + Positioned( + right: 200, + top: 1860.h, + child: Image.asset('assets/images/road_bottom.png'), + width: 380.w, + height: 900.h, + ), + Positioned( + left: 1002.w, + top: 4692.h, + child: Container( + transform: + Matrix4.identity() + ..translate(0.0, 0.0) + ..rotateZ(3.14), + width: 903.w, + height: 2328.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(), + child: Stack(), + ), + ), + Positioned( + left: 99.w, + top: 253.h, + child: Container( + width: 903.w, + height: 2328.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(), + child: Stack(), + ), + ), + Positioned( + // 첫 번째 동그라미 + left: 300.w, + top: 420.h, + child: Container( + width: 200.w, + height: 200.h, + decoration: ShapeDecoration( + color: Colors.white /* white */, + shape: OvalBorder(), + shadows: [ + BoxShadow( + color: Color(0x3F000000), + blurRadius: 15.r, + offset: Offset(0, 0), + spreadRadius: 0.r, + ), + ], + ), + ), + ), + Positioned( + // 네 번째 동그라미 + left: 150.w, + top: 1280.h, + child: Container( + width: 200.w, + height: 200.h, + decoration: ShapeDecoration( + color: Colors.white /* white */, + shape: OvalBorder(), + shadows: [ + BoxShadow( + color: Color(0x3F000000), + blurRadius: 15.r, + offset: Offset(0, 0), + spreadRadius: 0.r, + ), + ], + ), + ), + ), + Positioned( + // 일곱 번째 동그라미미 + left: 400.w, + top: 2100.h, + child: Container( + width: 200.w, + height: 200.h, + decoration: ShapeDecoration( + color: Colors.white /* white */, + shape: OvalBorder(), + shadows: [ + BoxShadow( + color: Color(0x3F000000), + blurRadius: 15.r, + offset: Offset(0, 0), + spreadRadius: 0.r, + ), + ], + ), + ), + ), + Positioned( + //세 번째 동그라미 + left: 530.w, + top: 980.h, + child: Container( + width: 200.w, + height: 200.h, + decoration: ShapeDecoration( + color: Colors.white /* white */, + shape: OvalBorder(), + shadows: [ + BoxShadow( + color: Color(0x3F000000), + blurRadius: 15.r, + offset: Offset(0, 0), + spreadRadius: 0.r, + ), + ], + ), + ), + ), + Positioned( + // 다섯 번째 동그라미미 + left: 500.w, + top: 1550.h, + child: Container( + width: 200.w, + height: 200.h, + decoration: ShapeDecoration( + color: Colors.white /* white */, + shape: OvalBorder(), + shadows: [ + BoxShadow( + color: Color(0x3F000000), + blurRadius: 15.r, + offset: Offset(0, 0), + spreadRadius: 0.r, + ), + ], + ), + ), + ), + Positioned( + // 두 번째 동그라미 + left: 710.w, + top: 621.h, + child: Container( + width: 200.w, + height: 200.h, + decoration: ShapeDecoration( + color: Colors.white /* white */, + shape: OvalBorder(), + shadows: [ + BoxShadow( + color: Color(0x3F000000), + blurRadius: 15.r, + offset: Offset(0, 0), + spreadRadius: 0.r, + ), + ], + ), + ), + ), + Positioned( + // 여섯 번째 동그라미 + left: 730.w, + top: 1850.h, + child: Container( + width: 200.w, + height: 200.h, + decoration: ShapeDecoration( + color: Colors.white /* white */, + shape: OvalBorder(), + shadows: [ + BoxShadow( + color: Color(0x3F000000), + blurRadius: 15.r, + offset: Offset(0, 0), + spreadRadius: 0.r, + ), + ], + ), + ), + ), + Positioned( + left: 0.w, + top: 0.h, + child: Container( + width: 1081.w, + height: 281.h, + decoration: BoxDecoration(color: Colors.white), + ), + ), + Positioned( + top: 120.h, + right: 30, + child: Container( + width: 1080.w, + height: 120.h, + padding: const EdgeInsets.only(top: 10), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 49.50.w, + children: [ + SizedBox( + width: 948.w, + child: Text( + 'STAGE 1', + style: TextStyle( + color: const Color(0xFF1C1C1C) /* main-black */, + fontSize: 60.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + letterSpacing: -0.60.w, + ), + ), + ), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/baking/questions/even/answer_block.dart b/lib/features/baking/UI/questions/even/answer_block.dart similarity index 95% rename from lib/features/baking/questions/even/answer_block.dart rename to lib/features/baking/UI/questions/even/answer_block.dart index e1d6c71..06b8491 100644 --- a/lib/features/baking/questions/even/answer_block.dart +++ b/lib/features/baking/UI/questions/even/answer_block.dart @@ -1,8 +1,8 @@ import 'package:componentss/core/user_provider.dart'; -import 'package:componentss/features/baking/baking_screen.dart'; -import 'package:componentss/features/baking/data/mission_api.dart'; -import 'package:componentss/features/baking/data/mission_model.dart'; -import 'package:componentss/features/baking/questions/even/answer_screen.dart'; +import 'package:componentss/features/baking/UI/baking_screen.dart'; +import 'package:componentss/features/baking/data/mission/mission_api.dart'; +import 'package:componentss/features/baking/data/mission/mission_model.dart'; +import 'package:componentss/features/baking/UI/questions/even/answer_screen.dart'; import 'package:componentss/features/main_screen.dart'; import 'package:componentss/icons/custom_icon_icons.dart'; import 'package:flutter/material.dart'; @@ -19,6 +19,7 @@ class AnswerBlock extends StatefulWidget { State createState() => _AnswerBlockState(); } +//로그인 로딩 처리 class _AnswerBlockState extends State { final TextEditingController _controller = TextEditingController(); @@ -75,7 +76,7 @@ class _AnswerBlockState extends State { TextSpan( children: [ TextSpan( - text: widget.mission.question, + text: "Q. ", style: TextStyle( color: const Color( 0xFFFF9F1C, diff --git a/lib/features/baking/questions/even/answer_screen.dart b/lib/features/baking/UI/questions/even/answer_screen.dart similarity index 97% rename from lib/features/baking/questions/even/answer_screen.dart rename to lib/features/baking/UI/questions/even/answer_screen.dart index f89a750..5ab34b2 100644 --- a/lib/features/baking/questions/even/answer_screen.dart +++ b/lib/features/baking/UI/questions/even/answer_screen.dart @@ -1,6 +1,6 @@ -import 'package:componentss/features/baking/baking_screen.dart'; -import 'package:componentss/features/baking/data/mission_model.dart'; -import 'package:componentss/features/baking/questions/even/answer_block.dart'; +import 'package:componentss/features/baking/UI/baking_screen.dart'; +import 'package:componentss/features/baking/data/mission/mission_model.dart'; +import 'package:componentss/features/baking/UI/questions/even/answer_block.dart'; import 'package:componentss/icons/custom_icon_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; diff --git a/lib/features/baking/questions/odd/odd_quiz.dart b/lib/features/baking/UI/questions/odd/odd_quiz.dart similarity index 97% rename from lib/features/baking/questions/odd/odd_quiz.dart rename to lib/features/baking/UI/questions/odd/odd_quiz.dart index e8e4cdc..d61a3ae 100644 --- a/lib/features/baking/questions/odd/odd_quiz.dart +++ b/lib/features/baking/UI/questions/odd/odd_quiz.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'package:componentss/core/user_provider.dart'; -import 'package:componentss/features/baking/baking_screen.dart'; -import 'package:componentss/features/baking/data/mission_api.dart'; -import 'package:componentss/features/baking/data/mission_model.dart'; -import 'package:componentss/features/baking/questions/trend/trend_quiz.dart'; +import 'package:componentss/features/baking/UI/baking_screen.dart'; +import 'package:componentss/features/baking/data/mission/mission_api.dart'; +import 'package:componentss/features/baking/data/mission/mission_model.dart'; import 'package:componentss/features/main_screen.dart'; import 'package:componentss/icons/custom_icon_icons.dart'; import 'package:flutter/material.dart'; diff --git a/lib/features/baking/questions/odd/odd_screen.dart b/lib/features/baking/UI/questions/odd/odd_screen.dart similarity index 94% rename from lib/features/baking/questions/odd/odd_screen.dart rename to lib/features/baking/UI/questions/odd/odd_screen.dart index ca01faf..cad12ca 100644 --- a/lib/features/baking/questions/odd/odd_screen.dart +++ b/lib/features/baking/UI/questions/odd/odd_screen.dart @@ -1,7 +1,7 @@ -import 'package:componentss/features/baking/baking_screen.dart'; -import 'package:componentss/features/baking/data/mission_model.dart'; -import 'package:componentss/features/baking/questions/odd/odd_quiz.dart'; -import 'package:componentss/features/baking/questions/trend/trend_quiz.dart'; +import 'package:componentss/features/baking/UI/baking_screen.dart'; +import 'package:componentss/features/baking/data/mission/mission_model.dart'; +import 'package:componentss/features/baking/UI/questions/odd/odd_quiz.dart'; +import 'package:componentss/features/main_screen.dart'; import 'package:componentss/icons/custom_icon_icons.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -151,8 +151,9 @@ class _OddScreenState extends State { context, MaterialPageRoute( builder: - (context) => - TrendQuiz(), // Replace with your actual screen + (context) => MainScreen( + goToPage: 1, + ), // Replace with your actual screen ), ); }, diff --git a/lib/features/baking/UI/setting/study_comeplete_screen.dart b/lib/features/baking/UI/setting/study_comeplete_screen.dart new file mode 100644 index 0000000..5364b76 --- /dev/null +++ b/lib/features/baking/UI/setting/study_comeplete_screen.dart @@ -0,0 +1,82 @@ +import 'package:componentss/features/main_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class StudyComplete extends StatelessWidget { + const StudyComplete({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 90), + Padding( + padding: const EdgeInsets.only(left: 13), + child: Text( + '면접 합격을 위한\n완벽한 준비를 마쳤어요', + style: TextStyle( + color: const Color(0xFF1C1C1C) /* main-black */, + fontSize: 76.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + ), + ), + ), + + Padding( + padding: const EdgeInsets.only(left: 13.0, top: 8), + child: Text( + "입력하신 정보들로 맞춤 퀘스트를 출제합니다!", + style: TextStyle( + fontSize: 44.sp, + fontWeight: FontWeight.w500, + color: Color(0xFF8E95A2), + ), + ), + ), + SizedBox(height: 550), + + GestureDetector( + onTap: () {}, + child: Center( + child: Container( + width: 993.w, + height: 160.h, + decoration: BoxDecoration( + color: Color(0XFFFF9F1C), + + borderRadius: BorderRadius.all(Radius.circular(33.r)), + ), + child: GestureDetector( + onTap: () { + Navigator.pushReplacement( + context, + MaterialPageRoute( + builder: (context) => MainScreen(goToPage: 0), + ), + ); + }, + child: Center( + child: Text( + "시작하기", + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontSize: 50.sp, + ), + ), + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/baking/UI/setting/study_make_level_screen.dart b/lib/features/baking/UI/setting/study_make_level_screen.dart new file mode 100644 index 0000000..351ff1f --- /dev/null +++ b/lib/features/baking/UI/setting/study_make_level_screen.dart @@ -0,0 +1,525 @@ +import 'package:componentss/features/baking/UI/setting/study_comeplete_screen.dart'; +import 'package:componentss/features/study/ui/make_group/study_make_group_name_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:componentss/icons/custom_icon_icons.dart'; + +class StudyMakeLevel extends StatefulWidget { + const StudyMakeLevel({super.key}); + + @override + State createState() => _StudyMakeLevelState(); +} + +class _StudyMakeLevelState extends State { + bool _isNextButtonClicked = false; // 버튼 상태를 부모에서 관리 + + String? _selectedStudyLevelText; + String? _selectedInclusionOptionText; + + bool _isNextButtonEnabled = false; + void _updateNextButtonState() { + setState(() { + _isNextButtonEnabled = + _selectedStudyLevelText != null && + _selectedInclusionOptionText != null; + }); + } + + void _handleStudyLevelSelect(String itemText) { + setState(() { + _selectedStudyLevelText = itemText; + _updateNextButtonState(); + }); + // 디버깅용 출력 + } + + void _handleInclusionOptionSelect(String itemText) { + setState(() { + _selectedInclusionOptionText = itemText; + _updateNextButtonState(); + }); + // 디버깅용 출력 + } + + void _handleNextButtonTap() { + final Map args = + ModalRoute.of(context)!.settings.arguments as Map; + final String category = args['category']; + final String category2 = args['category2']; + final String date = args['date']; + final String time = args['time']; + print('category: $category'); + print('category2: $category2'); + print('Selected Date: $date'); + print('Selected Time: $time'); + + // 1. 버튼 클릭 상태 변경 (UI 즉시 업데이트) + if (_isNextButtonEnabled) { + print("--- 다음 버튼 클릭 ---"); + print("선택된 난이도: ${_selectedStudyLevelText ?? '선택되지 않음'}"); + print("포함할까요?: ${_selectedInclusionOptionText ?? '선택되지 않음'}"); + print("--------------------"); + setState(() { + _isNextButtonClicked = true; + }); + + // 2. 다음 화면으로 이동하고, 돌아왔을 때 실행될 로직 추가 + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => StudyComplete(), + settings: RouteSettings( + arguments: { + 'category': category, + 'category2': category2, + 'date': date, + 'time': time, + 'studyLevel': _selectedStudyLevelText, + 'inclusionOption': _selectedInclusionOptionText, + }, + ), + ), + ).then((_) { + // StudyMakeGroup2 에서 돌아온 후에 이 코드가 실행됨 + // 위젯이 화면에 아직 마운트되어 있는지 확인 (중요) + if (mounted) { + // 3. 버튼 상태를 다시 false로 초기화 + setState(() { + _isNextButtonClicked = false; + }); + } + }); + } else { + print("다음 버튼 클릭 불가"); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + automaticallyImplyLeading: false, + leading: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Padding( + padding: EdgeInsets.only(top: 8), + child: Icon(CustomIcon.back, size: 18), + ), + ), + ), + + body: Padding( + padding: EdgeInsets.symmetric(horizontal: 77.w, vertical: 20.h), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 100.h), + Text( + '퀘스트 난이도를 선택해주세요', + style: TextStyle( + color: const Color(0xFF1F1F1F), + fontSize: 70.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + ), + ), + Text( + '준비 기간이 짧다면 난이도 중 이상을 추천해요', + style: TextStyle( + color: const Color(0xFF8E95A2), + fontSize: 44.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w500, + letterSpacing: -0.44, + ), + ), + SizedBox(height: 150.h), + Container( + width: 70.w, + height: 70.h, + decoration: ShapeDecoration( + color: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100.r), + ), + ), + child: Center( + child: Text( + '1', + style: TextStyle( + color: Colors.white, + fontSize: 50.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + ), + ), + ), + ), + SizedBox(height: 50.h), + StudyLevels(onItemSelected: _handleStudyLevelSelect), + SizedBox(height: 150.h), + Row( + children: [ + Container( + width: 70.w, + height: 70.h, + decoration: ShapeDecoration( + color: Colors.black, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(100.r), + ), + ), + child: Center( + child: Text( + '2', + style: TextStyle( + color: Colors.white, + fontSize: 50.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + ), + ), + ), + ), + + SizedBox(width: 40.w), + + Text( + '최근 이슈, 트랜드 퀴즈도 포함할까요?', + style: TextStyle( + color: Colors.black, + fontSize: 35.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + ), + ), + ], + ), + SizedBox(height: 50.h), + InclusionOptions(onItemSelected: _handleInclusionOptionSelect), + Spacer(), + Padding( + padding: EdgeInsets.only(bottom: 200.h), + child: Center( + child: NextButton( + isEnabled: _isNextButtonEnabled, + onTap: _handleNextButtonTap, + ), + ), + ), + ], + ), + ), + ); + } +} + +class StudyLevel extends StatefulWidget { + final String text; + final String assetPath; + final bool isSelected; + final Function(String) onSelected; + + const StudyLevel({ + super.key, + required this.text, + required this.assetPath, + required this.isSelected, + required this.onSelected, + }); + + @override + _StudyLevelState createState() => _StudyLevelState(); +} + +class _StudyLevelState extends State { + @override + Widget build(BuildContext context) { + Color containerColor = + widget.isSelected ? Colors.orange.withOpacity(0.3) : Colors.white; + Color borderColor = + widget.isSelected ? Colors.orange : const Color(0xFFEBEBEB); + + return GestureDetector( + onTap: () { + print("${widget.text} 클릭됨"); + widget.onSelected(widget.text); + }, + child: Container( + width: 450.w, + padding: EdgeInsets.only( + top: 46.h, + left: 50.w, + right: 50.w, + bottom: 46.h, + ), + decoration: ShapeDecoration( + color: containerColor, + shape: RoundedRectangleBorder( + side: BorderSide(width: 2.75.w, color: borderColor), + borderRadius: BorderRadius.circular(38.50.r), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + Container( + width: 100.w, + height: 100.w, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(widget.assetPath), + fit: BoxFit.cover, + ), + ), + ), + + SizedBox(width: 50.w), + Text( + widget.text, + style: TextStyle( + color: const Color(0xFF434343), + fontSize: 30.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w500, + letterSpacing: -0.40, + ), + ), + ], + ), + ), + ); + } +} + +class StudyLevels extends StatefulWidget { + final Function(String) onItemSelected; + + const StudyLevels({required this.onItemSelected, super.key}); + + @override + _StudyLevelsState createState() => _StudyLevelsState(); +} + +class LevelInfo { + final String text; + final String assetPath; + + LevelInfo({required this.text, required this.assetPath}); +} + +class _StudyLevelsState extends State { + String? selectedText; + + final List items = [ + LevelInfo(text: '난이도 상', assetPath: 'assets/images/box.png'), + LevelInfo(text: '난이도 중', assetPath: 'assets/images/box.png'), + LevelInfo(text: '난이도 하', assetPath: 'assets/images/box.png'), + LevelInfo(text: '난이도 기초', assetPath: 'assets/images/box.png'), + ]; + + void handleLevelSelected(String text) { + setState(() { + selectedText = text; + }); + widget.onItemSelected(text); + } + + @override + Widget build(BuildContext context) { + return Wrap( + alignment: WrapAlignment.center, + spacing: 15.w, + runSpacing: 15.h, + children: + items.map((itemInfo) { + return StudyLevel( + text: itemInfo.text, + assetPath: itemInfo.assetPath, + isSelected: selectedText == itemInfo.text, + onSelected: handleLevelSelected, + ); + }).toList(), + ); + } +} + +class InclusionOption extends StatefulWidget { + final String text; + final String assetPath; + final bool isSelected; + final Function(String) onSelected; + + const InclusionOption({ + super.key, + required this.text, + required this.assetPath, + required this.isSelected, + required this.onSelected, + }); + + @override + _InclusionOptionState createState() => _InclusionOptionState(); +} + +class _InclusionOptionState extends State { + @override + Widget build(BuildContext context) { + Color containerColor = + widget.isSelected ? Colors.orange.withOpacity(0.3) : Colors.white; + Color borderColor = + widget.isSelected ? Colors.orange : const Color(0xFFEBEBEB); + + return GestureDetector( + onTap: () { + print("${widget.text} 클릭됨"); + widget.onSelected(widget.text); + }, + child: Container( + width: 450.w, + padding: EdgeInsets.only( + top: 46.h, + left: 40.w, + right: 40.w, + bottom: 46.h, + ), + decoration: ShapeDecoration( + color: containerColor, + shape: RoundedRectangleBorder( + side: BorderSide(width: 2.75.w, color: borderColor), + borderRadius: BorderRadius.circular(38.50.r), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + Container( + width: 80.w, + height: 80.h, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(widget.assetPath), + fit: BoxFit.cover, + ), + ), + ), + SizedBox(width: 30.w), + Text( + widget.text, + + style: TextStyle( + color: const Color(0xFF434343), + fontSize: 30.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w500, + letterSpacing: -0.40, + ), + ), + ], + ), + ), + ); + } +} + +class InclusionOptions extends StatefulWidget { + final Function(String) onItemSelected; + + const InclusionOptions({required this.onItemSelected, super.key}); + + @override + _InclusionOptionsState createState() => _InclusionOptionsState(); +} + +class InclusionOptionInfo { + final String text; + final String assetPath; + + InclusionOptionInfo({required this.text, required this.assetPath}); +} + +class _InclusionOptionsState extends State { + String? selectedText; + + final List items = [ + InclusionOptionInfo(text: '포함합니다', assetPath: 'assets/images/box.png'), + InclusionOptionInfo(text: '포함하지 않습니다', assetPath: 'assets/images/box.png'), + ]; + + void handleInclusionOptionSelected(String text) { + setState(() { + selectedText = text; + }); + widget.onItemSelected(text); + } + + @override + Widget build(BuildContext context) { + return Wrap( + alignment: WrapAlignment.center, + spacing: 15.w, + runSpacing: 15.h, + children: + items.map((InclusionOptionInfo) { + return InclusionOption( + text: InclusionOptionInfo.text, + assetPath: InclusionOptionInfo.assetPath, + isSelected: selectedText == InclusionOptionInfo.text, + onSelected: handleInclusionOptionSelected, + ); + }).toList(), + ); + } +} + +class NextButton extends StatelessWidget { + final bool isEnabled; + final VoidCallback onTap; + + const NextButton({required this.isEnabled, required this.onTap, super.key}); + + @override + Widget build(BuildContext context) { + Color bgColor = isEnabled ? Color(0xFFFF9F1C) : Colors.white; + Color borderColor = isEnabled ? Colors.white : Color(0xFFFF9F1C); + Color textColor = isEnabled ? Colors.white : Color(0xFFFF9F1C); + + return InkWell( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + width: 993.w, + height: 160.h, + decoration: ShapeDecoration( + color: bgColor, + shape: RoundedRectangleBorder( + side: BorderSide(width: 2.75.w, color: borderColor), + borderRadius: BorderRadius.circular(33.r), + ), + ), + alignment: Alignment.center, + child: Text( + '다음', + textAlign: TextAlign.center, + style: TextStyle( + color: textColor, + fontSize: 50.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + letterSpacing: -0.50, + ), + ), + ), + ); + } +} diff --git a/lib/features/baking/UI/setting/study_make_screen.dart b/lib/features/baking/UI/setting/study_make_screen.dart new file mode 100644 index 0000000..23dfb1d --- /dev/null +++ b/lib/features/baking/UI/setting/study_make_screen.dart @@ -0,0 +1,462 @@ +import 'package:componentss/features/baking/UI/setting/study_make_screen2.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:componentss/features/study/ui/make_group/study_make_group_screen2.dart'; +import 'package:componentss/icons/custom_icon_icons.dart'; + +class StudyMake extends StatefulWidget { + const StudyMake({super.key}); + + @override + State createState() => _StudyMakeState(); +} + +class _StudyMakeState extends State { + bool _isNextButtonClicked = false; // 버튼 상태를 부모에서 관리 + + String? _selectedCategoryItemText; + String? _selectedCategoryChipText; + + bool _isNextButtonEnabled = false; + + void _updateNextButtonState() { + setState(() { + _isNextButtonEnabled = + _selectedCategoryItemText != null && + _selectedCategoryChipText != null; + }); + } + + void _handleCategoryItemSelect(String itemText) { + setState(() { + _selectedCategoryItemText = itemText; + _updateNextButtonState(); // 버튼 상태 업데이트 + }); + } + + void _handleCategoryChipSelect(String chipText) { + setState(() { + _selectedCategoryChipText = chipText; + _updateNextButtonState(); // 버튼 상태 업데이트 + }); + } + + // 다음 버튼 탭 처리 및 네비게이션 함수 + void _handleNextButtonTap() { + // 1. 버튼 클릭 상태 변경 (UI 즉시 업데이트) + + if (_isNextButtonEnabled) { + print("--- 다음 버튼 클릭 ---"); + print("선택된 단체: ${_selectedCategoryItemText ?? '선택되지 않음'}"); + print("선택된 분야: ${_selectedCategoryChipText ?? '선택되지 않음'}"); + print("--------------------"); + setState(() { + _isNextButtonClicked = true; + }); + + // 2. 다음 화면으로 이동하고, 돌아왔을 때 실행될 로직 추가 + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => StudyMake2(), + settings: RouteSettings( + arguments: { + 'category': _selectedCategoryItemText, + 'category2': _selectedCategoryChipText, + }, // 전달할 데이터 + ), + ), + ).then((_) { + // StudyMakeGroup2 에서 돌아온 후에 이 코드가 실행됨 + // 위젯이 화면에 아직 마운트되어 있는지 확인 (중요) + if (mounted) { + // 3. 버튼 상태를 다시 false로 초기화 + setState(() { + _isNextButtonClicked = false; + }); + } + }); + } else { + print("다음 버튼 클릭 불가"); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + leading: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Padding( + padding: EdgeInsets.only(top: 8), + child: Icon(CustomIcon.back, size: 18), + ), + ), + ), + body: Padding( + padding: EdgeInsets.symmetric(horizontal: 77.w), + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox(height: 100.h), + Text( + '어떤 면접을 앞두고 계신가요?', + style: TextStyle( + color: const Color(0xFF1F1F1F), + fontSize: 70.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + ), + ), + Text( + '준비하고 있는 단체와 분야를 알려주세요', + style: TextStyle( + color: const Color(0xFF8E95A2), + fontSize: 44.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w500, + letterSpacing: -0.44, + ), + ), + SizedBox(height: 120.h), + Container( + width: 992.w, + padding: (EdgeInsets.symmetric(horizontal: 50.w)), + child: CategoryItems( + onItemSelected: _handleCategoryItemSelect, + ), + ), + + SizedBox(height: 100.h), + Container( + padding: EdgeInsets.symmetric(horizontal: 77.w), + width: 991.w, + child: CategoryChipGroup( + onChipSelected: _handleCategoryChipSelect, + ), + ), + + Spacer(), + Padding( + padding: EdgeInsets.only(bottom: 200.h), + child: Center( + child: NextButton( + isEnabled: _isNextButtonEnabled, + onTap: _handleNextButtonTap, + ), + ), + ), + ], + ), + ], + ), + ), + ); + } +} + +class CategoryItem extends StatefulWidget { + final String text; + final String assetPath; + final bool isSelected; + final Function(String) onSelected; + + CategoryItem({ + required this.text, + required this.assetPath, + required this.isSelected, + required this.onSelected, + }); + + @override + _CategoryItemState createState() => _CategoryItemState(); +} + +class _CategoryItemState extends State { + @override + Widget build(BuildContext context) { + Color containerColor = + widget.isSelected ? Colors.orange.withOpacity(0.3) : Colors.white; + Color borderColor = + widget.isSelected ? Colors.orange : const Color(0xFFEBEBEB); + + return GestureDetector( + onTap: () { + print("${widget.text} 클릭됨"); + widget.onSelected(widget.text); + }, + child: Container( + width: 250.w, + height: 250.h, + padding: EdgeInsets.all(20.w), + decoration: ShapeDecoration( + color: containerColor, + shape: RoundedRectangleBorder( + side: BorderSide(width: 2.75.w, color: borderColor), + borderRadius: BorderRadius.circular(38.50.r), + ), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + + children: [ + Container( + width: 100.w, + height: 100.h, + decoration: BoxDecoration( + image: DecorationImage( + image: AssetImage(widget.assetPath), + fit: BoxFit.cover, + ), + ), + ), + SizedBox(height: 10.h), + Text( + widget.text, + style: TextStyle( + color: const Color(0xFF434343), + fontSize: 30.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w500, + letterSpacing: -0.44, + ), + ), + ], + ), + ), + ); + } +} + +class CategoryItems extends StatefulWidget { + final Function(String) onItemSelected; + + const CategoryItems({required this.onItemSelected, Key? key}) + : super(key: key); + + @override + _CategoryItemsState createState() => _CategoryItemsState(); +} + +class CategoryInfo { + final String text; + final String assetPath; + + CategoryInfo({required this.text, required this.assetPath}); +} + +class _CategoryItemsState extends State { + String? selectedText; + + final List items = [ + CategoryInfo(text: '교내동아리', assetPath: 'assets/images/box.png'), + CategoryInfo(text: '연합동아리', assetPath: 'assets/images/box.png'), + CategoryInfo(text: '서포터즈', assetPath: 'assets/images/box.png'), + CategoryInfo(text: '봉사단', assetPath: 'assets/images/box.png'), + CategoryInfo(text: '인턴 • 현장실습', assetPath: 'assets/images/box.png'), + CategoryInfo(text: '기타', assetPath: 'assets/images/box.png'), + ]; + + void handleItemSelected(String text) { + setState(() { + selectedText = text; + }); + widget.onItemSelected(text); + } + + @override + Widget build(BuildContext context) { + return Wrap( + alignment: WrapAlignment.start, + spacing: 15.w, + runSpacing: 15.h, + children: + items.map((itemInfo) { + return CategoryItem( + text: itemInfo.text, + assetPath: itemInfo.assetPath, + isSelected: selectedText == itemInfo.text, + onSelected: handleItemSelected, + ); + }).toList(), + ); + } +} + +class CategoryChip extends StatelessWidget { + final String emoji; + final String text; + final bool isSelected; + final Function(String) onSelected; + + CategoryChip({ + required this.emoji, + required this.text, + required this.isSelected, + required this.onSelected, + Key? key, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Color bgColor = isSelected ? const Color(0x21FF9F1C) : Colors.white; + Color borderColor = + isSelected ? const Color(0xFFFF9F1C) : const Color(0xFFEBEBEB); + + return GestureDetector( + onTap: () { + print("${text} 클릭됨"); + onSelected(text); + }, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 36.w, vertical: 26.h), + decoration: ShapeDecoration( + color: bgColor, + shape: RoundedRectangleBorder( + side: BorderSide(width: 2.75.w, color: borderColor), + borderRadius: BorderRadius.circular(36.r), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + emoji, + style: TextStyle( + color: const Color(0xFF1C1C1C), + fontSize: 36.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + letterSpacing: -0.36, + ), + ), + SizedBox(width: 10.w), + Text( + text, + style: TextStyle( + color: const Color(0xFF1C1C1C), + fontSize: 40.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + letterSpacing: -0.40, + ), + ), + ], + ), + ), + ); + } +} + +class CategoryChipInfo { + final String emoji; + final String text; + + CategoryChipInfo({required this.emoji, required this.text}); +} + +class CategoryChipGroup extends StatefulWidget { + final Function(String) onChipSelected; + + const CategoryChipGroup({required this.onChipSelected, Key? key}) + : super(key: key); + + @override + _CategoryChipGroupState createState() => _CategoryChipGroupState(); +} + +class _CategoryChipGroupState extends State { + String? selectedChipText; + + // 표시할 칩 데이터 목록 + final List chipItems = [ + CategoryChipInfo(emoji: '📚', text: '학술'), + CategoryChipInfo(emoji: '💻', text: '전공'), + CategoryChipInfo(emoji: '🎨', text: '예술'), + CategoryChipInfo(emoji: '👥', text: '문화•취미'), + CategoryChipInfo(emoji: '☀️', text: '봉사'), + CategoryChipInfo(emoji: '🔠', text: '어학'), + CategoryChipInfo(emoji: '🤝', text: '창업'), + CategoryChipInfo(emoji: '✈️', text: '여행'), + ]; + + void handleChipSelected(String text) { + setState(() { + selectedChipText = text; + }); + widget.onChipSelected(text); + } + + @override + Widget build(BuildContext context) { + return Wrap( + alignment: WrapAlignment.start, + runAlignment: WrapAlignment.center, + spacing: 20.w, + runSpacing: 20.h, + children: + chipItems.map((chipInfo) { + return CategoryChip( + emoji: chipInfo.emoji, + text: chipInfo.text, + // 현재 선택된 텍스트와 이 칩의 텍스트가 같은지 비교하여 isSelected 결정 + isSelected: selectedChipText == chipInfo.text, + + onSelected: handleChipSelected, + ); + }).toList(), + ); + } +} + +class NextButton extends StatelessWidget { + final bool isEnabled; + final VoidCallback onTap; + + const NextButton({required this.isEnabled, required this.onTap, Key? key}) + : super(key: key); + + @override + Widget build(BuildContext context) { + Color bgColor = isEnabled ? Color(0xFFFF9F1C) : Colors.white; + Color borderColor = isEnabled ? Colors.white : Color(0xFFFF9F1C); + Color textColor = isEnabled ? Colors.white : Color(0xFFFF9F1C); + + return InkWell( + onTap: onTap, + child: AnimatedContainer( + duration: const Duration(milliseconds: 200), + width: 993.w, + height: 160.h, + decoration: ShapeDecoration( + color: bgColor, + shape: RoundedRectangleBorder( + side: BorderSide(width: 2.75.w, color: borderColor), + borderRadius: BorderRadius.circular(33.r), + ), + ), + alignment: Alignment.center, + child: Text( + '다음', + textAlign: TextAlign.center, + style: TextStyle( + color: textColor, + fontSize: 50.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + letterSpacing: -0.50, + ), + ), + ), + ); + } +} diff --git a/lib/features/baking/UI/setting/study_make_screen2.dart b/lib/features/baking/UI/setting/study_make_screen2.dart new file mode 100644 index 0000000..2e56dbf --- /dev/null +++ b/lib/features/baking/UI/setting/study_make_screen2.dart @@ -0,0 +1,323 @@ +import 'package:componentss/features/baking/UI/setting/study_make_level_screen.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:intl/intl.dart'; +import 'package:componentss/features/study/ui/make_group/study_make_group_level_screen.dart'; +import 'package:componentss/icons/custom_icon_icons.dart'; + +class StudyMake2 extends StatefulWidget { + const StudyMake2({super.key}); + + @override + State createState() => _StudyMakeState2(); +} + +class _StudyMakeState2 extends State { + DateTime? _selectedDate; + TimeOfDay? _selectedTime; + + // 남은 일 수 계산 + int _calculateRemainingDays() { + if (_selectedDate == null) { + return 0; + } + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final interviewDate = DateTime( + _selectedDate!.year, + _selectedDate!.month, + _selectedDate!.day, + ); + final difference = interviewDate.difference(today).inDays; + return difference >= 0 ? difference : 0; + } + + // 날짜 선택 Bottom Sheet 표시 + void _showDatePicker(BuildContext context) { + DateTime initialDate = _selectedDate ?? DateTime.now(); + DateTime firstDate = DateTime.now().subtract(Duration(days: 1)); + DateTime lastDate = DateTime.now().add(Duration(days: 365 * 5)); + + showModalBottomSheet( + context: context, + builder: (BuildContext builder) { + DateTime tempDate = initialDate; + return Container( + height: MediaQuery.of(context).copyWith().size.height / 3, + color: Colors.white, + child: Column( + children: [ + Expanded( + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.date, + initialDateTime: initialDate, + minimumDate: firstDate, + maximumDate: lastDate, + onDateTimeChanged: (DateTime newDate) { + tempDate = newDate; + }, + ), + ), + CupertinoButton( + child: Text('확인', style: TextStyle(color: Colors.orange)), + onPressed: () { + setState(() { + _selectedDate = tempDate; + }); + Navigator.pop(context); + }, + ), + ], + ), + ); + }, + ); + } + + // 시간 선택 Bottom Sheet 표시 + void _showTimePicker(BuildContext context) { + DateTime initialDateTime = DateTime( + DateTime.now().year, + DateTime.now().month, + DateTime.now().day, + _selectedTime?.hour ?? DateTime.now().hour, + _selectedTime?.minute ?? DateTime.now().minute, + ); + + showModalBottomSheet( + context: context, + builder: (BuildContext builder) { + DateTime tempDateTime = initialDateTime; + return Container( + height: MediaQuery.of(context).copyWith().size.height / 3, + color: Colors.white, + child: Column( + children: [ + Expanded( + child: CupertinoDatePicker( + mode: CupertinoDatePickerMode.time, + initialDateTime: initialDateTime, + use24hFormat: true, + onDateTimeChanged: (DateTime newDateTime) { + tempDateTime = newDateTime; + }, + ), + ), + CupertinoButton( + child: Text('확인', style: TextStyle(color: Colors.orange)), + onPressed: () { + setState(() { + _selectedTime = TimeOfDay.fromDateTime(tempDateTime); + }); + Navigator.pop(context); + }, + ), + ], + ), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + final Map args = + ModalRoute.of(context)!.settings.arguments as Map; + final String category = args['category']; + final String category2 = args['category2']; + + final DateFormat dateFormatter = DateFormat('yyyy.MM.dd'); + final DateFormat timeFormatter = DateFormat('HH:mm'); + + String displayDate = + _selectedDate != null ? dateFormatter.format(_selectedDate!) : '날짜 선택'; + String displayTime = + _selectedTime != null + ? timeFormatter.format( + DateTime(2000, 1, 1, _selectedTime!.hour, _selectedTime!.minute), + ) + : '시간 선택'; + + int remainingDays = _calculateRemainingDays(); + + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + automaticallyImplyLeading: false, + leading: GestureDetector( + onTap: () { + Navigator.pop(context); + }, + child: Padding( + padding: EdgeInsets.only(top: 8), + child: Icon(CustomIcon.back, size: 18), + ), + ), + ), + body: Padding( + padding: EdgeInsets.symmetric(horizontal: 77.w, vertical: 50.h), + + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + + SizedBox(height: 100.h), + + Text( + '면접 날짜와 시간을 알려주세요', + style: TextStyle( + color: const Color(0xFF1F1F1F), + fontSize: 70.sp, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + ), + ), + SizedBox(height: 8.h), + Text( + '남은 날짜에 맞추어 준비를 도와드릴게요', + style: TextStyle( + color: const Color(0xFF8E95A2), + fontSize: 44.sp, + + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w500, + letterSpacing: -0.44, + ), + ), + SizedBox(height: 120.h), + // 날짜 선택 영역 + _buildDateTimePickerRow( + context: context, + displayText: displayDate, + icon: Icons.calendar_today_outlined, + onTap: () => _showDatePicker(context), + ), + SizedBox(height: 40.h), + // 시간 선택 영역 + _buildDateTimePickerRow( + context: context, + displayText: displayTime, + icon: Icons.access_time_outlined, + onTap: () => _showTimePicker(context), + ), + SizedBox(height: 70.h), + // 남은 날짜 표시 영역 + Container( + width: double.infinity, + + padding: EdgeInsets.symmetric(horizontal: 40.w, vertical: 40.h), + + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(33.r), // ScreenUtil 적용 + border: Border.all(color: Colors.grey[300]!), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Text('면접까지 ', style: TextStyle(fontSize: 45.sp)), + Text( + '$remainingDays일', + style: TextStyle( + fontSize: 45.sp, + fontWeight: FontWeight.bold, + color: Colors.orange, + ), + ), + Text(' 남음', style: TextStyle(fontSize: 45.sp)), + ], + ), + ), + + Spacer(), + Padding( + padding: EdgeInsets.only(bottom: 200.h), + child: SizedBox( + width: double.infinity, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: Colors.orange, + padding: EdgeInsets.symmetric(vertical: 45.h), + + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(33.r), + ), + elevation: 0, + ), + onPressed: + (_selectedDate != null && _selectedTime != null) + ? () { + print('category: $category'); + print('category2: $category2'); + print('Selected Date: $displayDate'); + print('Selected Time: $displayTime'); + print('Remaining Days: $remainingDays'); + Navigator.push( + context, + MaterialPageRoute(builder: (context) => StudyMakeLevel(), + settings: RouteSettings( + arguments: { + 'category': category, + 'category2': category2, + 'date': displayDate, + 'time': displayTime, + }, // 전달할 데이터 + ), + ), + + ); + } + : null, + child: Text( + '다음', + style: TextStyle( + fontSize: 50.sp, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ), + ], + ), + ), + ); + } + + // Helper 위젯 함수 + Widget _buildDateTimePickerRow({ + required BuildContext context, + required String displayText, + required IconData icon, + required VoidCallback onTap, + }) { + bool isSelected = displayText != '날짜 선택' && displayText != '시간 선택'; + return InkWell( + onTap: onTap, + child: Container( + padding: EdgeInsets.symmetric(horizontal: 40.w, vertical: 40.h), + decoration: BoxDecoration( + color: Colors.white, + + borderRadius: BorderRadius.circular(33.r), + border: Border.all(color: Colors.grey[300]!), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + displayText, + style: TextStyle( + fontSize: 45.sp, + color: isSelected ? Colors.black : Colors.grey[500], + ), + ), + Icon(icon, color: Colors.grey[600]), + ], + ), + ), + ); + } +} diff --git a/lib/features/baking/baking_screen.dart b/lib/features/baking/baking_screen.dart index 7feb3a2..5587ae2 100644 --- a/lib/features/baking/baking_screen.dart +++ b/lib/features/baking/baking_screen.dart @@ -1,16 +1,17 @@ import 'dart:math'; import 'package:componentss/core/user_provider.dart'; -import 'package:componentss/features/baking/data/attendance_api.dart'; -import 'package:componentss/features/baking/data/attendance_model.dart'; -import 'package:componentss/features/baking/data/mission_api.dart'; -import 'package:componentss/features/baking/data/mission_model.dart'; -import 'package:componentss/features/baking/data/mission_response_model.dart'; -import 'package:componentss/features/baking/questions/even/answer_screen.dart'; -import 'package:componentss/features/baking/questions/odd/odd_screen.dart'; +import 'package:componentss/features/baking/UI/questions/even/answer_screen.dart'; +import 'package:componentss/features/baking/UI/questions/odd/odd_screen.dart'; +import 'package:componentss/features/baking/data/attendacne/attendance_api.dart'; +import 'package:componentss/features/baking/data/attendacne/attendance_model.dart'; +import 'package:componentss/features/baking/data/mission/mission_api.dart'; +import 'package:componentss/features/baking/data/mission/mission_response_model.dart'; +//import 'package:componentss/features/study/ui/answer_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart'; +import 'package:intl/intl.dart'; class BakingScreen extends StatefulWidget { const BakingScreen({super.key}); @@ -302,52 +303,26 @@ class _BakingScreenState extends State { Stack( children: [ Container( - width: double.infinity, + //width: double.infinity, color: Colors.white, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 100), Text('logo'), + SizedBox(height: 50), + Center( - child: Container( - width: 1000.w, - height: 200.h, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 2, - ), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: Color(0x21FF9F1C), // 주황색 - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.96.w, - color: const Color( - 0xFFFF9F1C, - ) /* main-orange */, - ), - borderRadius: BorderRadius.circular(29.57.w), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 29.57.w, - children: [ - Text( - '동아리 면접', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.black, - fontSize: 36.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, - ), - ), - ], - ), + //API로 받아오기 + child: EventCard( + title: '동아리 면접 🍞', // 이모지도 텍스트로 넣을 수 있습니다. + targetDate: DateTime( + 2024, + 5, + 31, + 15, + 00, + ), // 목표 날짜 및 시간 ), ), @@ -521,10 +496,14 @@ class _BakingScreenState extends State { ), GestureDetector( onTap: () { - //Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) {} + // Navigator.push( + // context, + // MaterialPageRoute( + // builder: (context) { + // return ; + // }, + // ), + // ); }, child: Row( children: [ @@ -537,12 +516,12 @@ class _BakingScreenState extends State { ), ), SizedBox(height: 20), + //QnaListView(qnaItems: qnaList), //Padding( // padding: EdgeInsets.only(left: 20, right: 20), // child: Column( // children: [ - SizedBox(height: 100), ], ), ), @@ -645,3 +624,107 @@ class Quest { required this.isCompleted, }); } + +class EventCard extends StatelessWidget { + final String title; + final DateTime targetDate; + + const EventCard({Key? key, required this.title, required this.targetDate}) + : super(key: key); + + String calculateDday(DateTime target) { + final now = DateTime.now(); + final today = DateTime(now.year, now.month, now.day); + final targetDay = DateTime(target.year, target.month, target.day); + final difference = targetDay.difference(today).inDays; + + if (difference == 0) { + return 'D-Day'; // D-Day는 특별 처리 + } else if (difference > 0) { + return 'D - $difference'; + } else { + return 'D + ${difference.abs()}'; + } + } + + // D-day 문자열을 각 부분(문자)으로 분리하는 함수 + List getDdayParts(String dDayString) { + if (dDayString == 'D-Day') { + return ['D', '-', '0']; + } else { + // "D - 123" 또는 "D + 45" 같은 형태 처리 + List parts = []; + parts.add('D'); // 첫 글자 'D' + parts.add(dDayString.substring(2, 3)); // 부호 ('-' 또는 '+') + String numberPart = dDayString.substring(4); + parts.addAll(numberPart.split('')); + return parts; + } + } + + @override + Widget build(BuildContext context) { + final formattedDate = DateFormat('yyyy.MM.dd HH:mm').format(targetDate); + final dDayString = calculateDday(targetDate); + final dDayParts = getDdayParts(dDayString); // D-day 부분을 각 문자로 분리 + + return Container( + width: 1000.w, + height: 250.h, + + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0), + decoration: BoxDecoration( + color: Color(0x21FF9F1C), + borderRadius: BorderRadius.circular(15.0), + border: Border.all(color: Colors.orange, width: 1.5), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: TextStyle( + fontSize: 18.0, + fontWeight: FontWeight.bold, + color: Colors.black87, + ), + ), + SizedBox(height: 4.0), + Text( + formattedDate, + style: TextStyle(fontSize: 14.0, color: Colors.black54), + ), + ], + ), + + Row( + children: + dDayParts.map((part) { + // 각 문자(부분)를 위한 컨테이너 생성 + return Container( + margin: const EdgeInsets.only(left: 3.0), + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 5.0, + ), + decoration: BoxDecoration(color: Colors.white), + child: Text( + part, // D-day 문자 (예: 'D', '-', '3', '0') + style: TextStyle( + fontSize: 75.51.sp, + fontWeight: FontWeight.w900, + color: Colors.orange, + ), + ), + ); + }).toList(), + ), + ], + ), + ); + } +} diff --git a/lib/features/baking/data/attendance_api.dart b/lib/features/baking/data/attendacne/attendance_api.dart similarity index 93% rename from lib/features/baking/data/attendance_api.dart rename to lib/features/baking/data/attendacne/attendance_api.dart index a035a2c..b5acc7f 100644 --- a/lib/features/baking/data/attendance_api.dart +++ b/lib/features/baking/data/attendacne/attendance_api.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'package:componentss/features/baking/data/attendance_model.dart'; +import 'package:componentss/features/baking/data/attendacne/attendance_model.dart'; import 'package:http/http.dart' as http; class AttendanceApi { diff --git a/lib/features/baking/data/attendance_model.dart b/lib/features/baking/data/attendacne/attendance_model.dart similarity index 100% rename from lib/features/baking/data/attendance_model.dart rename to lib/features/baking/data/attendacne/attendance_model.dart diff --git a/lib/features/baking/data/mission_api.dart b/lib/features/baking/data/mission/mission_api.dart similarity index 94% rename from lib/features/baking/data/mission_api.dart rename to lib/features/baking/data/mission/mission_api.dart index a97f0a2..7d216fd 100644 --- a/lib/features/baking/data/mission_api.dart +++ b/lib/features/baking/data/mission/mission_api.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'dart:math'; -import 'package:componentss/features/baking/data/mission_model.dart'; -import 'package:componentss/features/baking/data/mission_response_model.dart'; +import 'package:componentss/features/baking/data/mission/mission_model.dart'; +import 'package:componentss/features/baking/data/mission/mission_response_model.dart'; import 'package:http/http.dart' as http; const String baseUrl = 'http://34.64.233.128:5200'; diff --git a/lib/features/baking/data/mission_model.dart b/lib/features/baking/data/mission/mission_model.dart similarity index 100% rename from lib/features/baking/data/mission_model.dart rename to lib/features/baking/data/mission/mission_model.dart diff --git a/lib/features/baking/data/mission_response_model.dart b/lib/features/baking/data/mission/mission_response_model.dart similarity index 88% rename from lib/features/baking/data/mission_response_model.dart rename to lib/features/baking/data/mission/mission_response_model.dart index e81613e..d99ccbb 100644 --- a/lib/features/baking/data/mission_response_model.dart +++ b/lib/features/baking/data/mission/mission_response_model.dart @@ -1,5 +1,5 @@ -import 'package:componentss/features/baking/data/mission_model.dart'; +import 'package:componentss/features/baking/data/mission/mission_model.dart'; class MissionResponse { final Mission nextOddMission; diff --git a/lib/features/baking/questions/trend/trend_ox.dart b/lib/features/baking/questions/trend/trend_ox.dart deleted file mode 100644 index 5df414d..0000000 --- a/lib/features/baking/questions/trend/trend_ox.dart +++ /dev/null @@ -1,213 +0,0 @@ -import 'package:componentss/features/baking/baking_screen.dart'; -import 'package:componentss/features/baking/questions/trend/trend_quiz.dart'; -import 'package:componentss/features/main_screen.dart'; -import 'package:componentss/icons/custom_icon_icons.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; - -class TrendOx extends StatefulWidget { - const TrendOx({super.key}); - - @override - State createState() => _TrendOxState(); -} - -class _TrendOxState extends State { - bool isOSelected = false; // O 버튼 선택 상태 - bool isXSelected = false; // X 버튼 선택 상태 - String? selectedAnswer; // 선택된 답안 ("O" 또는 "X") - - // 서버로 답안을 전송하는 메서드 - Future submitAnswer(String answer) async { - try { - // 서버 요청 로직 (예: HTTP POST 요청) - print("서버로 전송된 답안: $answer"); - // 예: await http.post(Uri.parse("http://example.com/submit"), body: {"answer": answer}); - } catch (e) { - print("답안 전송 중 오류 발생: $e"); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - width: 1080.w, - height: 2400.h, - decoration: BoxDecoration(color: const Color(0xFFFAFAFA)), - child: Column( - children: [ - // 상단 뒤로가기 버튼 - Padding( - padding: EdgeInsets.only(top: 140.h, left: 35.w), - child: Align( - alignment: Alignment.topLeft, - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => MainScreen(goToPage: 0), - ), - ); - }, - child: Icon(CustomIcon.back, size: 55.w), - ), - ), - ), - SizedBox(height: 50.h), // 간격 추가 - // 질문 영역 - Container( - width: 992.w, - height: 522.h, - padding: const EdgeInsets.all(2), - decoration: ShapeDecoration( - color: Colors.white, - shape: RoundedRectangleBorder( - side: BorderSide(width: 3.w, color: const Color(0xFFEBEBEB)), - borderRadius: BorderRadius.circular(33.r), - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 11, - vertical: 3, - ), - decoration: ShapeDecoration( - color: const Color(0xFFFF9F1C), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(52.94.r), - ), - ), - child: Text( - 'Q1', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white, - fontSize: 48.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - ), - ), - ), - SizedBox(height: 20.h), - SizedBox( - width: 904.w, - child: Text( - '정보가 파악되지 않아 사회가 공식적으로 계측하는 경제활동 추계에 포함되지 않는 경제활동은?', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color(0xFF1C1C1C), - fontSize: 45.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w500, - height: 1.40, - ), - ), - ), - ], - ), - ), - SizedBox(height: 100.h), // 간격 추가 - // X 버튼 - GestureDetector( - onTap: () { - setState(() { - isOSelected = false; // O 버튼 선택 해제 - isXSelected = true; // X 버튼 선택 - selectedAnswer = "X"; // 선택된 답안 설정 - }); - submitAnswer("X"); // 서버로 답안 전송 - }, - child: Container( - width: 992.w, - height: 409.h, - padding: const EdgeInsets.all(44), - decoration: ShapeDecoration( - color: - isXSelected - ? const Color(0xFFFF9F1C) // 선택된 경우 노란색 - : Colors.white, // 기본 흰색 - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.75.w, - color: const Color(0xFFFF9F1C), - ), - borderRadius: BorderRadius.circular(33.r), - ), - ), - child: Center( - child: Text( - 'X', - textAlign: TextAlign.center, - style: TextStyle( - color: - isXSelected - ? Colors - .white // 선택된 경우 글자 흰색 - : const Color(0xFFFF9F1C), // 기본 노란색 - fontSize: 120.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, - letterSpacing: -1.20.w, - ), - ), - ), - ), - ), - SizedBox(height: 50.h), // 간격 추가 - // O 버튼 - GestureDetector( - onTap: () { - setState(() { - isOSelected = true; // O 버튼 선택 - isXSelected = false; // X 버튼 선택 해제 - selectedAnswer = "O"; // 선택된 답안 설정 - }); - submitAnswer("O"); // 서버로 답안 전송 - }, - child: Container( - width: 992.w, - height: 409.h, - padding: const EdgeInsets.all(44), - decoration: ShapeDecoration( - color: - isOSelected - ? const Color(0xFFFF9F1C) // 선택된 경우 노란색 - : Colors.white, // 기본 흰색 - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.75.w, - color: const Color(0xFFFF9F1C), - ), - borderRadius: BorderRadius.circular(33.r), - ), - ), - child: Center( - child: Text( - 'O', - textAlign: TextAlign.center, - style: TextStyle( - color: - isOSelected - ? Colors - .white // 선택된 경우 글자 흰색 - : const Color(0xFFFF9F1C), // 기본 노란색 - fontSize: 120.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, - letterSpacing: -1.20.w, - ), - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/features/baking/questions/trend/trend_quiz.dart b/lib/features/baking/questions/trend/trend_quiz.dart deleted file mode 100644 index 2aacef2..0000000 --- a/lib/features/baking/questions/trend/trend_quiz.dart +++ /dev/null @@ -1,179 +0,0 @@ -import 'package:componentss/features/baking/baking_screen.dart'; -import 'package:componentss/features/main_screen.dart'; -import 'package:componentss/icons/custom_icon_icons.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; - -class TrendQuiz extends StatefulWidget { - const TrendQuiz({super.key}); - - @override - State createState() => _TrendQuizState(); -} - -class _TrendQuizState extends State { - String? selectedAnswer; // 선택된 답안 (예: "1", "2", "3", "4") - - // 서버로 답안을 전송하는 메서드 - Future submitAnswer(String answer) async { - try { - // 서버 요청 로직 (예: HTTP POST 요청) - print("서버로 전송된 답안: $answer"); - // 예: await http.post(Uri.parse("http://example.com/submit"), body: {"answer": answer}); - } catch (e) { - print("답안 전송 중 오류 발생: $e"); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - width: 1080.w, - height: 2400.h, - decoration: BoxDecoration(color: const Color(0xFFFAFAFA)), - child: Column( - children: [ - // 상단 뒤로가기 버튼 - Padding( - padding: EdgeInsets.only(top: 140.h, left: 35.w), - child: Align( - alignment: Alignment.topLeft, - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => MainScreen(goToPage: 0), - ), - ); - }, - child: Icon(CustomIcon.back, size: 55.w), - ), - ), - ), - SizedBox(height: 50.h), // 간격 추가 - // 질문 영역 - Container( - width: 992.w, - height: 522.h, - padding: const EdgeInsets.all(2), - decoration: ShapeDecoration( - color: Colors.white, - shape: RoundedRectangleBorder( - side: BorderSide(width: 3.w, color: const Color(0xFFEBEBEB)), - borderRadius: BorderRadius.circular(33.r), - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.symmetric( - horizontal: 11, - vertical: 3, - ), - decoration: ShapeDecoration( - color: const Color(0xFFFF9F1C), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(52.94.r), - ), - ), - child: Text( - 'Q1', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white, - fontSize: 48.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - ), - ), - ), - SizedBox(height: 20.h), - SizedBox( - width: 904.w, - child: Text( - '정보가 파악되지 않아 사회가 공식적으로 계측하는 경제활동 추계에 포함되지 않는 경제활동은?', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color(0xFF1C1C1C), - fontSize: 45.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w500, - height: 1.40, - ), - ), - ), - ], - ), - ), - SizedBox(height: 100.h), // 간격 추가 - // 선택지 버튼들 (2x2 레이아웃) - Expanded( - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 45.w), - child: Wrap( - spacing: 20.w, // 가로 간격 - runSpacing: 20.h, // 세로 간격 - alignment: WrapAlignment.center, - children: [ - buildAnswerButton("1", "규모의 경제"), - buildAnswerButton("2", "지하경제"), - buildAnswerButton("3", "범위의 경제"), - buildAnswerButton("4", "갈라파고스 경제"), - ], - ), - ), - ), - ], - ), - ), - ); - } - - // 선택지 버튼 생성 메서드 - Widget buildAnswerButton(String answer, String text) { - final bool isSelected = selectedAnswer == answer; - - return GestureDetector( - onTap: () { - setState(() { - selectedAnswer = answer; // 선택된 답안 설정 - }); - submitAnswer(answer); // 서버로 답안 전송 - }, - child: Container( - width: 450.w, // 버튼 너비 - height: 450.h, // 버튼 높이 - decoration: ShapeDecoration( - color: - isSelected - ? const Color(0xFFFF9F1C) // 선택된 경우 노란색 - : Colors.white, // 기본 흰색 - shape: RoundedRectangleBorder( - side: BorderSide(width: 2.75.w, color: const Color(0xFFFF9F1C)), - borderRadius: BorderRadius.circular(33.r), - ), - ), - child: Center( - child: Text( - text, - textAlign: TextAlign.center, - style: TextStyle( - color: - isSelected - ? Colors - .white // 선택된 경우 글자 흰색 - : const Color(0xFFFF9F1C), // 기본 노란색 - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - letterSpacing: -0.50.w, - ), - ), - ), - ), - ); - } -} diff --git a/lib/features/baking/questions/trend/trend_screen.dart b/lib/features/baking/questions/trend/trend_screen.dart deleted file mode 100644 index a85aee2..0000000 --- a/lib/features/baking/questions/trend/trend_screen.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:componentss/features/baking/baking_screen.dart'; -import 'package:componentss/features/baking/questions/trend/trend_quiz.dart'; -import 'package:componentss/icons/custom_icon_icons.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; - -class TrendScreen extends StatefulWidget { - const TrendScreen({super.key}); - - @override - State createState() => _trendScreenState(); -} - -class _trendScreenState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - width: 1080.w, - height: 2400.h, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(color: Colors.white), - child: Stack( - children: [ - Positioned( - top: 140.h, - left: 35.w, - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: - (context) => - BakingScreen(), // Replace with your actual screen - ), - ); - }, - child: Icon(CustomIcon.back, size: 55.w), - ), - ), - Positioned( - left: 45.w, - top: 341.h, - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 4, - ), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: const Color(0xFFEBEBEB) /* light-gray */, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(29.57.w), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - //spacing: 29.57.w, - children: [ - Text( - '최근 트렌드 학습하기', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color(0xFF6B6B6B) /* dark-gray */, - fontSize: 36.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ), - ), - Positioned( - left: 45.w, - top: 451.62.h, - child: Text( - '경제 관련 시사 내용 퀴즈', - style: TextStyle( - color: const Color(0xFF1C1C1C) /* main-black */, - fontSize: 76.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, - ), - ), - ), - Positioned( - left: 45.w, - top: 600.h, - child: Text( - '최근 일주일 간 면접에서 많이 언급된 시사 트렌드를\nOX퀴즈, 객관식으로 공부해보아요!', - style: TextStyle( - color: const Color(0xFF8E95A2), - fontSize: 44.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w500, - height: 4.h, - letterSpacing: -0.44.w, - ), - ), - ), - Positioned( - left: 44.w, - top: 2032.h, - child: GestureDetector( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: - (context) => - TrendQuiz(), // Replace with your actual screen - ), - ); - }, - child: Container( - width: 993.w, - height: 160.h, - padding: const EdgeInsets.all(10), - decoration: ShapeDecoration( - color: const Color(0xFFFF9F1C) /* main-orange */, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(33.w), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - //spacing: 27.50.w, - children: [ - Text( - '문제 풀기', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - letterSpacing: -0.50.w, - ), - ), - ], - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/lib/features/pages_list.dart b/lib/features/pages_list.dart index 5e6aea8..954f360 100644 --- a/lib/features/pages_list.dart +++ b/lib/features/pages_list.dart @@ -1,11 +1,7 @@ -import 'package:componentss/features/baking/baking_screen.dart'; +import 'package:componentss/features/baking/UI/baking_screen.dart'; +import 'package:componentss/features/baking/UI/setting/study_make_screen.dart'; import 'package:componentss/features/profile/my_profie_screen.dart'; import 'package:componentss/features/search/search_screen.dart'; import 'package:componentss/features/study/ui/study_screen.dart'; -List pages = [ - StudyScreen(), - BakingScreen(), - SearchScreen(), - MyProfieScreen(), -]; +List pages = [BakingScreen(), StudyScreen(), SearchScreen(), MyProfieScreen()]; diff --git a/lib/features/study/data/group_api.dart b/lib/features/study/data/group_api.dart index 8de4d64..b21121c 100644 --- a/lib/features/study/data/group_api.dart +++ b/lib/features/study/data/group_api.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:componentss/features/study/data/ranking_model.dart'; import 'package:http/http.dart' as http; import 'group_model.dart'; // GroupModel 파일 import @@ -77,3 +78,55 @@ class GroupApi { } } } + +Future> fetchMemberScores(List memberIds) async { + Map memberScores = {}; + + for (String memberId in memberIds) { + final url = Uri.parse("http://34.64.233.128:5200/missions/score/$memberId"); + + try { + final response = await http.get( + url, + headers: {'Content-Type': 'application/json'}, + ); + + if (response.statusCode == 200) { + final int score = int.parse(response.body.trim()); + memberScores[memberId] = score; + print("점수 가져오기 성공"); + } else { + print('❌ 점수 가져오기 실패: ${response.statusCode}'); + } + } catch (e) { + print('❌ 요청 중 오류 발생: $e'); + } + } + + return memberScores; +} + +Future> fetchDailyRanking(String groupId) async { + final String baseUrl = "http://34.64.233.128:5200"; + final url = Uri.parse("$baseUrl/missions/groups/$groupId/daily-ranking"); + print(groupId); + + try { + print("🔄 랭킹 데이터 요청 시작: $url"); + final response = await http + .get(url, headers: {'Content-Type': 'application/json'}) + .timeout(Duration(seconds: 10)); + + if (response.statusCode == 200) { + final List data = jsonDecode(response.body); + print("✅ 랭킹 데이터 가져오기 성공: $data"); + return data.map((item) => RankingModel.fromJson(item)).toList(); + } else { + print("Error: ${response.statusCode}, ${response.body}"); + return []; + } + } catch (e) { + print("GET 요청 실패: $e"); + return []; + } +} diff --git a/lib/features/study/data/group_model.dart b/lib/features/study/data/group_model.dart index 73f5c38..3e0067e 100644 --- a/lib/features/study/data/group_model.dart +++ b/lib/features/study/data/group_model.dart @@ -1,40 +1,55 @@ import 'dart:convert'; class GroupModel { - final String authorId; - final String name; - final String? joinCode; - final List tags; - final String? imageUrl; + final String id; // 그룹 ID + final String authorId; // 작성자 ID + final String name; // 그룹 이름 + final String joinCode; // 그룹 가입 코드 + final List tags; // 태그 리스트 + final String? imageUrl; // 이미지 URL + final List memberIds; // 멤버 ID 리스트 + final int score; // 그룹 점수 + final int level; // 그룹 레벨 GroupModel({ + required this.id, required this.authorId, required this.name, - + required this.joinCode, required this.tags, this.imageUrl, - this.joinCode, + required this.memberIds, + required this.score, + required this.level, }); // JSON → 객체 변환 factory GroupModel.fromJson(Map json) { return GroupModel( + id: json['id'], authorId: json['authorId'], name: json['name'], joinCode: json['joinCode'], tags: List.from(json['tags'] ?? []), imageUrl: json['imageUrl'], + memberIds: List.from(json['memberIds'] ?? []), + score: json['score'] ?? 0, + level: json['level'] ?? 0, ); } // 객체 → JSON 변환 Map toJson() { return { + 'id': id, 'authorId': authorId, 'name': name, 'joinCode': joinCode, 'tags': tags, 'imageUrl': imageUrl, + 'memberIds': memberIds, + 'score': score, + 'level': level, }; } @@ -47,4 +62,4 @@ class GroupModel { String toJsonString() { return jsonEncode(toJson()); } -} +} \ No newline at end of file diff --git a/lib/features/study/data/ranking_model.dart b/lib/features/study/data/ranking_model.dart new file mode 100644 index 0000000..c3b4534 --- /dev/null +++ b/lib/features/study/data/ranking_model.dart @@ -0,0 +1,22 @@ +class RankingModel { + final String userId; + final String userName; + final int score; + final int rank; + + RankingModel({ + required this.userId, + required this.userName, + required this.score, + required this.rank, + }); + + factory RankingModel.fromJson(Map json) { + return RankingModel( + userId: json['userId'], + userName: json['userName'], + score: json['score'], + rank: json['rank'], + ); + } +} diff --git a/lib/features/study/data/tempGroup.dart b/lib/features/study/data/tempGroup.dart deleted file mode 100644 index 3ca4870..0000000 --- a/lib/features/study/data/tempGroup.dart +++ /dev/null @@ -1,17 +0,0 @@ -class Group { - final String id; - final String name; - final String description; - final String imageUrl; - final String meetingInfo; - final String memberCount; - - Group({ - required this.id, - required this.name, - required this.description, - required this.imageUrl, - required this.meetingInfo, - required this.memberCount, - }); -} diff --git a/lib/features/study/ui/group_detail/detail_home.dart b/lib/features/study/ui/group_detail/detail_home.dart index 4257f69..89f1366 100644 --- a/lib/features/study/ui/group_detail/detail_home.dart +++ b/lib/features/study/ui/group_detail/detail_home.dart @@ -1,5 +1,6 @@ import 'dart:math'; +import 'package:componentss/features/study/data/group_api.dart'; import 'package:componentss/features/study/data/group_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -14,440 +15,625 @@ class DetailHome extends StatefulWidget { } class _DetailHomeState extends State { + GroupModel? _group; + Map _memberScores = {}; + + Future loadGroupAndScores(String joinCode) async { + final groupApi = GroupApi(); + final group = await groupApi.getGroupByJoinCode(joinCode); + print(group); + + if (group != null) { + final scores = await fetchMemberScores(group.memberIds); + + setState(() { + _group = group; + _memberScores = scores; + }); + } else { + print('❌ 그룹 정보를 가져오지 못했습니다.'); + } + } + @override - Widget build(BuildContext context) { - return Stack( - children: [ - Expanded( + void initState() { + super.initState(); + loadGroupAndScores(widget.groupModel.joinCode); + } + + // Widget _buildMemberList() { + // if (_group == null || _memberScores.isEmpty) { + // return Center(child: CircularProgressIndicator()); + // } + + // return ListView.builder( + // itemCount: _group!.memberIds.length, + // itemBuilder: (context, index) { + // final memberId = _group!.memberIds[index]; + // final score = _memberScores[memberId] ?? 0; + + // return ListTile( + // leading: CircleAvatar( + // backgroundColor: Colors.grey, + // child: Text( + // memberId.substring(0, 2), // 멤버 ID의 일부를 표시 + // style: TextStyle(color: Colors.white), + // ), + // ), + // title: Text( + // 'Member ID: $memberId', + // style: TextStyle(color: Colors.white), + // ), + // trailing: Text('$score 점', style: TextStyle(color: Colors.orange)), + // ); + // }, + // ); + // } + + Widget _buildMemberList() { + if (_group == null || _memberScores.isEmpty) { + return Center(child: CircularProgressIndicator()); + } + + if (_group!.memberIds.isEmpty) { + return Center( + child: Text( + '멤버가 없습니다.', + style: TextStyle(color: Colors.white, fontSize: 18), + ), + ); + } + + return ListView.builder( + shrinkWrap: true, // 부모 위젯의 크기에 맞게 리스트뷰 크기 조정 + physics: NeverScrollableScrollPhysics(), // 스크롤 비활성화 (필요 시 제거) + itemCount: _group!.memberIds.length, + itemBuilder: (context, index) { + final memberId = _group!.memberIds[index]; + final score = _memberScores[memberId] ?? 0; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), child: Container( - width: double.infinity, - color: Color(0xFF434343), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + padding: EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Colors.grey[800], + borderRadius: BorderRadius.circular(12.0), + ), + child: Row( children: [ - Row( + CircleAvatar( + backgroundColor: Colors.grey, + child: Text( + memberId.substring(0, 2), // 멤버 ID의 일부를 표시 + style: TextStyle(color: Colors.white), + ), + ), + SizedBox(width: 16.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - width: 130.w, - height: 154.h, - margin: EdgeInsets.symmetric(horizontal: 8.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(35.r), - ), + Text( + 'Member ID: $memberId', + style: TextStyle(color: Colors.white, fontSize: 16), ), - - Container( - width: 130.w, - height: 154.h, - margin: EdgeInsets.symmetric(horizontal: 8.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(35.r), - ), - ), - - Container( - width: 130.w, - height: 154.h, - margin: EdgeInsets.symmetric(horizontal: 8.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(35.r), - ), + Text( + 'Score: $score', + style: TextStyle(color: Colors.orange, fontSize: 16), ), ], ), - Stack( + ], + ), + ), + ); + }, + ); + } + + Widget _buildSingleMember() { + if (_group == null || _memberScores.isEmpty) { + return Center(child: CircularProgressIndicator()); + } + + // 첫 번째 멤버 가져오기 + final memberId = _group!.memberIds.isNotEmpty ? _group!.memberIds[0] : null; + final score = memberId != null ? _memberScores[memberId] ?? 0 : 0; + + if (memberId == null) { + return Center( + child: Text( + '멤버가 없습니다.', + style: TextStyle(color: Colors.white, fontSize: 18), + ), + ); + } + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Container( + padding: EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Colors.grey[800], + borderRadius: BorderRadius.circular(12.0), + ), + child: Row( + children: [ + CircleAvatar( + backgroundColor: Colors.grey, + child: Text( + memberId.substring(0, 2), // 멤버 ID의 일부를 표시 + style: TextStyle(color: Colors.white), + ), + ), + SizedBox(width: 16.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Member ID: $memberId', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + Text( + 'Score: $score', + style: TextStyle(color: Colors.orange, fontSize: 16), + ), + ], + ), + ], + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Stack( + children: [ + Expanded( + child: Container( + width: double.infinity, + color: Color(0xFF434343), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Center(child: AnimatedHalfCircleProgress()), - Padding( - padding: EdgeInsets.only(top: 90), - child: Center( - child: Container( - width: 611.w, - height: 500.h, - decoration: BoxDecoration(color: Color(0XFF6B6B6B)), + Row( + children: [ + Container( + width: 130.w, + height: 154.h, + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(35.r), + ), ), - ), - ), - ], - ), - SizedBox(height: 20), - Center( - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 2, - ), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: const Color(0x21FF9F1C), - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.96.w, - color: const Color(0xFFFF9F1C) /* main-orange */, + + Container( + width: 130.w, + height: 154.h, + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(35.r), + ), ), - borderRadius: BorderRadius.circular(29.57.w), - ), + + Container( + width: 130.w, + height: 154.h, + margin: EdgeInsets.symmetric(horizontal: 8.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(35.r), + ), + ), + ], ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 29.57.w, + Stack( children: [ - Text( - 'Lv.0', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color(0xFFFF9F1C) /* main-orange */, - fontSize: 36.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, + Center(child: AnimatedHalfCircleProgress()), + Padding( + padding: EdgeInsets.only(top: 90), + child: Center( + child: Container( + width: 611.w, + height: 500.h, + decoration: BoxDecoration( + color: Color(0XFF6B6B6B), + ), + ), ), ), ], ), - ), - ), - SizedBox(height: 6), - Center( - child: Text( - '따끈따끈한 반죽', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 66.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, - ), - ), - ), - SizedBox(height: 40), - Padding( - padding: EdgeInsets.only(left: 20), - child: Text( - '오늘 획득한 그룹 포인트', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, - ), - ), - ), - SizedBox(height: 20), - Stack( - children: [ + SizedBox(height: 20), Center( - child: Opacity( - opacity: 0.10.w, - child: Container( - width: 992.w, - height: 522.h, - decoration: ShapeDecoration( - color: const Color(0xFFD9D9D9), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(38.50.w), + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 2, + ), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + color: const Color(0x21FF9F1C), + shape: RoundedRectangleBorder( + side: BorderSide( + width: 2.96.w, + color: const Color(0xFFFF9F1C) /* main-orange */, ), + borderRadius: BorderRadius.circular(29.57.w), ), ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 29.57.w, + children: [ + Text( + 'Lv.0', + textAlign: TextAlign.center, + style: TextStyle( + color: const Color( + 0xFFFF9F1C, + ) /* main-orange */, + fontSize: 36.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w400, + ), + ), + ], + ), ), ), + SizedBox(height: 6), Center( - child: Padding( - padding: EdgeInsets.only(top: 15), - child: SizedBox( - width: 868.w, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 315.w, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 42.w, - children: [ - Container( - width: 150.w, - height: 150.h, - decoration: ShapeDecoration( - image: DecorationImage( - image: NetworkImage( - "https://placehold.co/150x150", - ), - fit: BoxFit.cover, - ), - shape: OvalBorder( - side: BorderSide( - width: 1.68.w, - color: const Color( - 0xFFC4CAD4, - ) /* gray */, - ), - ), - ), - ), - SizedBox( - width: 241.w, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - spacing: 23.w, - children: [ - SizedBox( - width: 241.w, - height: 60.h, - child: Text( - 'Username', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - ), - ), - ), - Container( - width: 123.31.w, - height: 66.65.h, - padding: const EdgeInsets.symmetric( - horizontal: 5, - vertical: 3, - ), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: const Color( - 0xFF6B6B6B, - ) /* dark-gray */, - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.96.w, - color: const Color( - 0xFFC4CAD4, - ) /* gray */, - ), - borderRadius: - BorderRadius.circular( - 29.57.w, - ), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - spacing: 29.57.w, - children: [ - Text( - 'level', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color( - 0xFFC4CAD4, - ) /* gray */, - fontSize: 36.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), - Text( - '20 p', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color( - 0xFFFF9F1C, - ) /* main-orange */, - fontSize: 55.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w900, - letterSpacing: -0.55.w, - ), - ), - ], - ), + child: Text( + '따끈따끈한 반죽', + style: TextStyle( + color: Colors.white /* white */, + fontSize: 66.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, ), ), ), - Center( - child: Padding( - padding: EdgeInsets.only(top: 120), - child: SizedBox( - width: 868.w, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 31.w, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 42.w, - children: [ - Container( - width: 150.w, - height: 150.h, - decoration: ShapeDecoration( - image: DecorationImage( - image: NetworkImage( - "https://placehold.co/150x150", - ), - fit: BoxFit.cover, - ), - shape: OvalBorder( - side: BorderSide( - width: 1.68.w, - color: const Color( - 0xFFC4CAD4, - ) /* gray */, - ), - ), - ), - ), - SizedBox( - width: 241.w, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.start, - crossAxisAlignment: - CrossAxisAlignment.start, - spacing: 23.w, - children: [ - SizedBox( - width: 241.w, - height: 60.h, - child: Text( - 'Username', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - ), - ), - ), - Container( - width: 123.31.w, - height: 66.65.h, - padding: const EdgeInsets.symmetric( - horizontal: 5, - vertical: 3, - ), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: const Color( - 0xFF6B6B6B, - ) /* dark-gray */, - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.96.w, - color: const Color( - 0xFFC4CAD4, - ) /* gray */, - ), - borderRadius: - BorderRadius.circular( - 29.57.w, - ), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: - MainAxisAlignment.center, - crossAxisAlignment: - CrossAxisAlignment.center, - spacing: 29.57.w, - children: [ - Text( - 'level', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color( - 0xFFC4CAD4, - ) /* gray */, - fontSize: 36.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), - Text( - '20 p', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color( - 0xFFFF9F1C, - ) /* main-orange */, - fontSize: 55.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w900, - letterSpacing: -0.55.w, - ), - ), - ], - ), + SizedBox(height: 40), + Padding( + padding: EdgeInsets.only(left: 20), + child: Text( + '오늘 획득한 그룹 포인트', + style: TextStyle( + color: Colors.white /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, ), ), ), + SizedBox(height: 20), + + // Stack( + // children: [ + // Center( + // child: Opacity( + // opacity: 0.10.w, + // child: Container( + // width: 992.w, + // height: 522.h, + // decoration: ShapeDecoration( + // color: const Color(0xFFD9D9D9), + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(38.50.w), + // ), + // ), + // ), + // ), + // ), + // Center( + // child: Padding( + // padding: EdgeInsets.only(top: 15), + // child: SizedBox( + // width: 868.w, + // child: Row( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // crossAxisAlignment: CrossAxisAlignment.center, + // spacing: 315.w, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.center, + // spacing: 42.w, + // children: [ + // Container( + // width: 150.w, + // height: 150.h, + // decoration: ShapeDecoration( + // image: DecorationImage( + // image: NetworkImage( + // "https://placehold.co/150x150", + // ), + // fit: BoxFit.cover, + // ), + // shape: OvalBorder( + // side: BorderSide( + // width: 1.68.w, + // color: const Color( + // 0xFFC4CAD4, + // ) /* gray */, + // ), + // ), + // ), + // ), + // SizedBox( + // width: 241.w, + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment.start, + // crossAxisAlignment: + // CrossAxisAlignment.start, + // spacing: 23.w, + // children: [ + // SizedBox( + // width: 241.w, + // height: 60.h, + // child: Text( + // 'Username', + // style: TextStyle( + // color: Colors.white /* white */, + // fontSize: 50.w, + // fontFamily: 'Wanted Sans', + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Container( + // width: 123.31.w, + // height: 66.65.h, + // padding: const EdgeInsets.symmetric( + // horizontal: 5, + // vertical: 3, + // ), + // clipBehavior: Clip.antiAlias, + // decoration: ShapeDecoration( + // color: const Color( + // 0xFF6B6B6B, + // ) /* dark-gray */, + // shape: RoundedRectangleBorder( + // side: BorderSide( + // width: 2.96.w, + // color: const Color( + // 0xFFC4CAD4, + // ) /* gray */, + // ), + // borderRadius: + // BorderRadius.circular( + // 29.57.w, + // ), + // ), + // ), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment.center, + // crossAxisAlignment: + // CrossAxisAlignment.center, + // spacing: 29.57.w, + // children: [ + // Text( + // 'level', + // textAlign: TextAlign.center, + // style: TextStyle( + // color: const Color( + // 0xFFC4CAD4, + // ) /* gray */, + // fontSize: 36.w, + // fontFamily: 'Wanted Sans', + // fontWeight: FontWeight.w400, + // ), + // ), + // ], + // ), + // ), + // ], + // ), + // ), + // ], + // ), + // Text( + // '20 p', + // textAlign: TextAlign.center, + // style: TextStyle( + // color: const Color( + // 0xFFFF9F1C, + // ) /* main-orange */, + // fontSize: 55.w, + // fontFamily: 'Wanted Sans', + // fontWeight: FontWeight.w900, + // letterSpacing: -0.55.w, + // ), + // ), + // ], + // ), + // ), + // ), + // ), + // Center( + // child: Padding( + // padding: EdgeInsets.only(top: 120), + // child: SizedBox( + // width: 868.w, + // child: Row( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.spaceBetween, + // crossAxisAlignment: CrossAxisAlignment.center, + // spacing: 31.w, + // children: [ + // Row( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: MainAxisAlignment.start, + // crossAxisAlignment: CrossAxisAlignment.center, + // spacing: 42.w, + // children: [ + // Container( + // width: 150.w, + // height: 150.h, + // decoration: ShapeDecoration( + // image: DecorationImage( + // image: NetworkImage( + // "https://placehold.co/150x150", + // ), + // fit: BoxFit.cover, + // ), + // shape: OvalBorder( + // side: BorderSide( + // width: 1.68.w, + // color: const Color( + // 0xFFC4CAD4, + // ) /* gray */, + // ), + // ), + // ), + // ), + // SizedBox( + // width: 241.w, + // child: Column( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment.start, + // crossAxisAlignment: + // CrossAxisAlignment.start, + // spacing: 23.w, + // children: [ + // SizedBox( + // width: 241.w, + // height: 60.h, + // child: Text( + // 'Username', + // style: TextStyle( + // color: Colors.white /* white */, + // fontSize: 50.w, + // fontFamily: 'Wanted Sans', + // fontWeight: FontWeight.w600, + // ), + // ), + // ), + // Container( + // width: 123.31.w, + // height: 66.65.h, + // padding: const EdgeInsets.symmetric( + // horizontal: 5, + // vertical: 3, + // ), + // clipBehavior: Clip.antiAlias, + // decoration: ShapeDecoration( + // color: const Color( + // 0xFF6B6B6B, + // ) /* dark-gray */, + // shape: RoundedRectangleBorder( + // side: BorderSide( + // width: 2.96.w, + // color: const Color( + // 0xFFC4CAD4, + // ) /* gray */, + // ), + // borderRadius: + // BorderRadius.circular( + // 29.57.w, + // ), + // ), + // ), + // child: Row( + // mainAxisSize: MainAxisSize.min, + // mainAxisAlignment: + // MainAxisAlignment.center, + // crossAxisAlignment: + // CrossAxisAlignment.center, + // spacing: 29.57.w, + // children: [ + // Text( + // 'level', + // textAlign: TextAlign.center, + // style: TextStyle( + // color: const Color( + // 0xFFC4CAD4, + // ) /* gray */, + // fontSize: 36.w, + // fontFamily: 'Wanted Sans', + // fontWeight: FontWeight.w400, + // ), + // ), + // ], + // ), + // ), + // ], + // ), + // ), + // ], + // ), + // Text( + // '20 p', + // textAlign: TextAlign.center, + // style: TextStyle( + // color: const Color( + // 0xFFFF9F1C, + // ) /* main-orange */, + // fontSize: 55.w, + // fontFamily: 'Wanted Sans', + // fontWeight: FontWeight.w900, + // letterSpacing: -0.55.w, + // ), + // ), + // ], + // ), + // ), + // ), + // ), + // ], + // ), + SizedBox( + height: 300, + width: 300, + child: _buildMemberList(), + ), ], ), - ], + ), ), - ), - ), - Positioned( - right: 24, - top: 7, - child: Icon(Icons.circle, size: 40, color: Color(0xffFF9F1C)), - ), - Positioned( - right: 20, - child: SizedBox( - width: 120.w, - height: 120.h, - child: Stack( - children: [ - Positioned( - left: 31.20.w, - top: 34.80.h, - child: Text( - '27', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 48.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - letterSpacing: -0.48.w, + Positioned( + right: 24, + top: 7, + child: Icon(Icons.circle, size: 40, color: Color(0xffFF9F1C)), + ), + Positioned( + right: 20, + child: SizedBox( + width: 120.w, + height: 120.h, + child: Stack( + children: [ + Positioned( + left: 31.20.w, + top: 34.80.h, + child: Text( + '27', + style: TextStyle( + color: Colors.white /* white */, + fontSize: 48.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + letterSpacing: -0.48.w, + ), + ), ), - ), + ], ), - ], + ), ), - ), + ], ), ], ); diff --git a/lib/features/study/ui/group_detail/detail_ranking.dart b/lib/features/study/ui/group_detail/detail_ranking.dart index 67b437d..9852fcd 100644 --- a/lib/features/study/ui/group_detail/detail_ranking.dart +++ b/lib/features/study/ui/group_detail/detail_ranking.dart @@ -1,4 +1,6 @@ +import 'package:componentss/features/study/data/group_api.dart'; import 'package:componentss/features/study/data/group_model.dart'; +import 'package:componentss/features/study/data/ranking_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; @@ -12,390 +14,375 @@ class DetailRanking extends StatefulWidget { } class _DetailRankingState extends State { + List _rankings = []; + bool _isLoading = true; @override - Widget build(BuildContext context) { - return Container( - child: Stack( - children: [ - Positioned( - child: Container( - width: 1080.w, - height: 2906.h, - decoration: BoxDecoration(color: const Color(0xFF434343)), + void initState() { + super.initState(); + _loadRankingData(); // 랭킹 데이터를 로드 + } + + Future _loadRankingData() async { + print("🔄 랭킹 데이터 로드 시작"); + final rankings = await fetchDailyRanking(widget.groupModel.id); + + setState(() { + _rankings = rankings; + _isLoading = false; + }); + } + + Widget _buildRankingList() { + if (_isLoading) { + return Center(child: CircularProgressIndicator()); + } + + if (_rankings.isEmpty) { + return Center( + child: Text( + '랭킹 데이터가 없습니다.', + style: TextStyle(color: Colors.black, fontSize: 18), + ), + ); + } + + return ListView.builder( + itemCount: _rankings.length, + itemBuilder: (context, index) { + final ranking = _rankings[index]; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Container( + padding: EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Colors.grey[800], + borderRadius: BorderRadius.circular(12.0), ), - ), - Positioned( - left: 0.w, - top: 0.h, - child: SizedBox( - width: 1080.w, - height: 105.h, - child: Stack( - children: [ - Positioned( - left: 726.w, - top: 36.h, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 6.w, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + children: [ + CircleAvatar( + backgroundColor: Colors.grey, + child: Text( + ranking.rank.toString(), // 랭크 표시 + style: TextStyle(color: Colors.white), + ), + ), + SizedBox(width: 16.0), + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - width: 45.w, - height: 45.h, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(), - child: Stack(), + Text( + ranking.userName, + style: TextStyle(color: Colors.white, fontSize: 16), ), - Container( - width: 48.w, - height: 48.h, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(), - child: Stack(), - ), - Container( - width: 45.w, - height: 45.h, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(), - child: Stack(), - ), - Container( - width: 45.w, - height: 45.h, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(), - child: Stack(), - ), - Container( - width: 45.w, - height: 45.h, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(), - child: Stack(), - ), - Container( - transform: - Matrix4.identity() - ..translate(0.0, 0.0) - ..rotateZ(-1.57), - height: 48.h, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration(), - child: Stack(), + Text( + 'Score: ${ranking.score}', + style: TextStyle(color: Colors.orange, fontSize: 16), ), ], ), - ), - ], - ), + ], + ), + Text( + '#${ranking.rank}', + style: TextStyle(color: Colors.white, fontSize: 16), + ), + ], ), ), - Positioned( - // 첫 번째 박스 - left: 44.w, - top: 208.h, - child: Opacity( - opacity: 0.10.w, - child: Container( - width: 992.w, - height: 240.h, - decoration: ShapeDecoration( - color: const Color(0xFFD9D9D9), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(38.50.w), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Container( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: _buildRankingList(), + ), + Stack( + children: [ + Positioned( + child: Container( + width: 1080.w, + height: 2906.h, + decoration: BoxDecoration(color: const Color(0xFF434343)), + ), + ), + Positioned( + left: 0.w, + top: 0.h, + child: SizedBox( + width: 1080.w, + height: 105.h, + child: Stack( + children: [ + Positioned( + left: 726.w, + top: 36.h, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 6.w, + children: [ + Container( + width: 45.w, + height: 45.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(), + child: Stack(), + ), + Container( + width: 48.w, + height: 48.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(), + child: Stack(), + ), + Container( + width: 45.w, + height: 45.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(), + child: Stack(), + ), + Container( + width: 45.w, + height: 45.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(), + child: Stack(), + ), + Container( + width: 45.w, + height: 45.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(), + child: Stack(), + ), + Container( + transform: + Matrix4.identity() + ..translate(0.0, 0.0) + ..rotateZ(-1.57), + height: 48.h, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration(), + child: Stack(), + ), + ], + ), + ), + ], ), ), ), - ), - ), - Positioned( - // 세 번째 박스 - left: 44.w, - top: 1128.h, - child: Opacity( - opacity: 0.10.w, - child: Container( - width: 992.w, - height: 240.h, - decoration: ShapeDecoration( - color: const Color(0xFFD9D9D9), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(38.50.w), + Positioned( + // 첫 번째 박스 + left: 44.w, + top: 208.h, + child: Opacity( + opacity: 0.10.w, + child: Container( + width: 992.w, + height: 240.h, + decoration: ShapeDecoration( + color: const Color(0xFFD9D9D9), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(38.50.w), + ), + ), ), ), ), - ), - ), - Positioned( - // 두 번째 박스 - left: 44.w, - top: 480.h, - child: Opacity( - opacity: 0.10.w, - child: Container( - width: 992.w, - height: 240.h, - decoration: ShapeDecoration( - color: const Color(0xFFD9D9D9), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(38.50.w), + Positioned( + // 세 번째 박스 + left: 44.w, + top: 1128.h, + child: Opacity( + opacity: 0.10.w, + child: Container( + width: 992.w, + height: 240.h, + decoration: ShapeDecoration( + color: const Color(0xFFD9D9D9), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(38.50.w), + ), + ), ), ), ), - ), - ), - Positioned( - //네 번째 박스 - left: 44.w, - top: 1400.h, - child: Opacity( - opacity: 0.10.w, - child: Container( - width: 992.w, - height: 240.h, - decoration: ShapeDecoration( - color: const Color(0xFFD9D9D9), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(38.50.w), + Positioned( + // 두 번째 박스 + left: 44.w, + top: 480.h, + child: Opacity( + opacity: 0.10.w, + child: Container( + width: 992.w, + height: 240.h, + decoration: ShapeDecoration( + color: const Color(0xFFD9D9D9), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(38.50.w), + ), + ), + ), + ), + ), + Positioned( + //네 번째 박스 + left: 44.w, + top: 1400.h, + child: Opacity( + opacity: 0.10.w, + child: Container( + width: 992.w, + height: 240.h, + decoration: ShapeDecoration( + color: const Color(0xFFD9D9D9), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(38.50.w), + ), + ), ), ), ), - ), - ), - Positioned( - left: 66.w, - top: 80.h, - child: Text( - '우리 스터디 순위는?', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, + Positioned( + left: 66.w, + top: 80.h, + child: Text( + '우리 스터디 순위는?', + style: TextStyle( + color: Colors.white /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + ), + ), ), - ), - ), - Positioned( - left: 66.w, - top: 1000.h, - child: Text( - '오늘 제일 열심히 준비한 팀원은?', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, + Positioned( + left: 66.w, + top: 1000.h, + child: Text( + '오늘 제일 열심히 준비한 팀원은?', + style: TextStyle( + color: Colors.white /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + ), + ), ), - ), - ), - Positioned( - left: 193.w, - top: 248.h, - child: Opacity( - opacity: 0.50.w, - child: SizedBox( - width: 800.w, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( + Positioned( + left: 193.w, + top: 248.h, + child: Opacity( + opacity: 0.50.w, + child: SizedBox( + width: 800.w, + child: Row( mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, - spacing: 42.w, children: [ - Container( - width: 150.w, - height: 150.h, - decoration: ShapeDecoration( - image: DecorationImage( - image: NetworkImage( - "https://placehold.co/150x150", - ), - fit: BoxFit.cover, - ), - shape: OvalBorder( - side: BorderSide( - width: 1.68.w, - color: const Color(0xFFC4CAD4) /* gray */, - ), - ), - ), - ), - SizedBox( - width: 300.w, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 23.w, - children: [ - Text( - 'Groupname', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - ), - ), - Container( - width: 123.31.w, - height: 66.65.h, - padding: const EdgeInsets.symmetric( - horizontal: 5, - vertical: 3, - ), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: const Color( - 0xFF6B6B6B, //첫번째 level 박스 - ) /* dark-gray */, - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.96.w, - color: const Color(0xFFC4CAD4) /* gray */, - ), - borderRadius: BorderRadius.circular( - 29.57.w, - ), - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 29.57.w, - children: [ - Text( - 'level', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color( - 0xFFffffff, - ) /* gray */, - fontSize: 36.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, - ), - ), - ], - ), - ), - ], - ), - ), - ], - ), - Text( - '1000 p', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, - letterSpacing: -0.50.w, - ), - ), - ], - ), - ), - ), - ), - Positioned( - left: 193.w, - top: 1168.h, - child: SizedBox( - width: 800.w, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 42.w, - children: [ - Container( - width: 150.w, - height: 150.h, - decoration: ShapeDecoration( - image: DecorationImage( - image: NetworkImage("https://placehold.co/150x150"), - fit: BoxFit.cover, - ), - shape: OvalBorder( - side: BorderSide( - width: 1.68.w, - color: const Color(0xFFC4CAD4) /* gray */, - ), - ), - ), - ), - SizedBox( - width: 241.w, - child: Column( + Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 23.w, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 42.w, children: [ - SizedBox( - width: 241.w, - height: 60.h, - child: Text( - 'Username', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - ), - ), - ), Container( - width: 123.31.w, - height: 66.65.h, - padding: const EdgeInsets.symmetric( - horizontal: 5, - vertical: 3, - ), - clipBehavior: Clip.antiAlias, + width: 150.w, + height: 150.h, decoration: ShapeDecoration( - color: const Color( - 0xFF6B6B6B, - ) /* dark-gray */, // 세 번째 level 박스 - shape: RoundedRectangleBorder( + image: DecorationImage( + image: NetworkImage( + "https://placehold.co/150x150", + ), + fit: BoxFit.cover, + ), + shape: OvalBorder( side: BorderSide( - width: 2.96.w, + width: 1.68.w, color: const Color(0xFFC4CAD4) /* gray */, ), - borderRadius: BorderRadius.circular(29.57.w), ), ), - child: Row( + ), + SizedBox( + width: 300.w, + child: Column( mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 29.57.w, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 23.w, children: [ Text( - 'level', - textAlign: TextAlign.center, + 'Groupname', style: TextStyle( - color: const Color(0xFFFFFFFF), - fontSize: 36.w, + color: Colors.white /* white */, + fontSize: 50.w, fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, + fontWeight: FontWeight.w600, + ), + ), + Container( + width: 123.31.w, + height: 66.65.h, + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 3, + ), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + color: const Color( + 0xFF6B6B6B, //첫번째 level 박스 + ) /* dark-gray */, + shape: RoundedRectangleBorder( + side: BorderSide( + width: 2.96.w, + color: const Color( + 0xFFC4CAD4, + ) /* gray */, + ), + borderRadius: BorderRadius.circular( + 29.57.w, + ), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + spacing: 29.57.w, + children: [ + Text( + 'level', + textAlign: TextAlign.center, + style: TextStyle( + color: const Color( + 0xFFffffff, + ) /* gray */, + fontSize: 36.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w400, + ), + ), + ], ), ), ], @@ -403,306 +390,443 @@ class _DetailRankingState extends State { ), ], ), - ), - ], - ), - Text( - '20 p', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, - letterSpacing: -0.50.w, + Text( + '1000 p', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + letterSpacing: -0.50.w, + ), + ), + ], ), ), - ], + ), ), - ), - ), - Positioned( - left: 193.w, - top: 520.h, - child: SizedBox( - width: 800.w, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( + Positioned( + left: 193.w, + top: 1168.h, + child: SizedBox( + width: 800.w, + child: Row( mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, - spacing: 42.w, children: [ - Container( - width: 150.w, - height: 150.h, - decoration: ShapeDecoration( - image: DecorationImage( - image: NetworkImage("https://placehold.co/150x150"), - fit: BoxFit.cover, - ), - shape: OvalBorder( - side: BorderSide( - width: 1.68.w, - color: const Color(0xFFC4CAD4) /* gray */, - ), - ), - ), - ), - SizedBox( - width: 300.w, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 23.w, - children: [ - Text( - 'Groupname', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - ), - ), - Container( - width: 123.31.w, - height: 66.65.h, - padding: const EdgeInsets.symmetric( - horizontal: 5, - vertical: 3, + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 42.w, + children: [ + Container( + width: 150.w, + height: 150.h, + decoration: ShapeDecoration( + image: DecorationImage( + image: NetworkImage( + "https://placehold.co/150x150", + ), + fit: BoxFit.cover, ), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: const Color( - 0xFF6B6B6B, - ) /* dark-gray */, // 두번째 level 박스 - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.96.w, - color: const Color(0xFFC4CAD4) /* gray */, - ), - borderRadius: BorderRadius.circular(29.57.w), + shape: OvalBorder( + side: BorderSide( + width: 1.68.w, + color: const Color(0xFFC4CAD4) /* gray */, ), ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 29.57.w, - children: [ - Text( - 'level', - textAlign: TextAlign.center, + ), + ), + SizedBox( + width: 241.w, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 23.w, + children: [ + SizedBox( + width: 241.w, + height: 60.h, + child: Text( + 'Username', style: TextStyle( - color: const Color(0xFFFFFFFF) /* gray */, - fontSize: 36.w, + color: Colors.white /* white */, + fontSize: 50.w, fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, + fontWeight: FontWeight.w600, ), ), - ], - ), + ), + Container( + width: 123.31.w, + height: 66.65.h, + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 3, + ), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + color: const Color( + 0xFF6B6B6B, + ) /* dark-gray */, // 세 번째 level 박스 + shape: RoundedRectangleBorder( + side: BorderSide( + width: 2.96.w, + color: const Color( + 0xFFC4CAD4, + ) /* gray */, + ), + borderRadius: BorderRadius.circular( + 29.57.w, + ), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + spacing: 29.57.w, + children: [ + Text( + 'level', + textAlign: TextAlign.center, + style: TextStyle( + color: const Color(0xFFFFFFFF), + fontSize: 36.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ], ), - ], + ), + ], + ), + Text( + '20 p', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + letterSpacing: -0.50.w, ), ), ], ), - Text( - '988 p', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, - letterSpacing: -0.50.w, - ), - ), - ], + ), ), - ), - ), - Positioned( - left: 193.w, - top: 1440.h, - child: SizedBox( - width: 800.w, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( + Positioned( + left: 193.w, + top: 520.h, + child: SizedBox( + width: 800.w, + child: Row( mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, - spacing: 42.w, children: [ - Container( - width: 150.w, - height: 150.h, - decoration: ShapeDecoration( - image: DecorationImage( - image: NetworkImage("https://placehold.co/150x150"), - fit: BoxFit.cover, + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 42.w, + children: [ + Container( + width: 150.w, + height: 150.h, + decoration: ShapeDecoration( + image: DecorationImage( + image: NetworkImage( + "https://placehold.co/150x150", + ), + fit: BoxFit.cover, + ), + shape: OvalBorder( + side: BorderSide( + width: 1.68.w, + color: const Color(0xFFC4CAD4) /* gray */, + ), + ), + ), ), - shape: OvalBorder( - side: BorderSide( - width: 1.68.w, - color: const Color(0xFFC4CAD4) /* gray */, + SizedBox( + width: 300.w, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 23.w, + children: [ + Text( + 'Groupname', + style: TextStyle( + color: Colors.white /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + ), + ), + Container( + width: 123.31.w, + height: 66.65.h, + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 3, + ), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + color: const Color( + 0xFF6B6B6B, + ) /* dark-gray */, // 두번째 level 박스 + shape: RoundedRectangleBorder( + side: BorderSide( + width: 2.96.w, + color: const Color( + 0xFFC4CAD4, + ) /* gray */, + ), + borderRadius: BorderRadius.circular( + 29.57.w, + ), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + spacing: 29.57.w, + children: [ + Text( + 'level', + textAlign: TextAlign.center, + style: TextStyle( + color: const Color( + 0xFFFFFFFF, + ) /* gray */, + fontSize: 36.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ], ), ), + ], + ), + Text( + '988 p', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + letterSpacing: -0.50.w, ), ), - SizedBox( - width: 241.w, - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - spacing: 23.w, - children: [ - SizedBox( - width: 241.w, - height: 60.h, - child: Text( - 'Username', - style: TextStyle( - color: Colors.white /* white */, - fontSize: 50.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, + ], + ), + ), + ), + Positioned( + left: 193.w, + top: 1440.h, + child: SizedBox( + width: 800.w, + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + spacing: 42.w, + children: [ + Container( + width: 150.w, + height: 150.h, + decoration: ShapeDecoration( + image: DecorationImage( + image: NetworkImage( + "https://placehold.co/150x150", ), + fit: BoxFit.cover, ), - ), - Container( - width: 123.31.w, - height: 66.65.h, - padding: const EdgeInsets.symmetric( - horizontal: 5, - vertical: 3, - ), - clipBehavior: Clip.antiAlias, - decoration: ShapeDecoration( - color: const Color( - 0xFF6B6B6B, - ) /* dark-gray */, // 네 번째 level 박스 - shape: RoundedRectangleBorder( - side: BorderSide( - width: 2.96.w, - color: const Color(0xFFC4CAD4) /* gray */, - ), - borderRadius: BorderRadius.circular(29.57.w), + shape: OvalBorder( + side: BorderSide( + width: 1.68.w, + color: const Color(0xFFC4CAD4) /* gray */, ), ), - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - spacing: 29.57.w, - children: [ - Text( - 'level', - textAlign: TextAlign.center, + ), + ), + SizedBox( + width: 241.w, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + spacing: 23.w, + children: [ + SizedBox( + width: 241.w, + height: 60.h, + child: Text( + 'Username', style: TextStyle( - color: const Color(0xFFFfffff) /* gray */, - fontSize: 36.w, + color: Colors.white /* white */, + fontSize: 50.w, fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w400, + fontWeight: FontWeight.w600, ), ), - ], - ), + ), + Container( + width: 123.31.w, + height: 66.65.h, + padding: const EdgeInsets.symmetric( + horizontal: 5, + vertical: 3, + ), + clipBehavior: Clip.antiAlias, + decoration: ShapeDecoration( + color: const Color( + 0xFF6B6B6B, + ) /* dark-gray */, // 네 번째 level 박스 + shape: RoundedRectangleBorder( + side: BorderSide( + width: 2.96.w, + color: const Color( + 0xFFC4CAD4, + ) /* gray */, + ), + borderRadius: BorderRadius.circular( + 29.57.w, + ), + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + spacing: 29.57.w, + children: [ + Text( + 'level', + textAlign: TextAlign.center, + style: TextStyle( + color: const Color( + 0xFFFfffff, + ) /* gray */, + fontSize: 36.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w400, + ), + ), + ], + ), + ), + ], ), - ], + ), + ], + ), + Text( + '15 p', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white /* white */, + fontSize: 50.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w700, + letterSpacing: -0.50.h, ), ), ], ), - Text( - '15 p', + ), + ), + Positioned( + left: 85.w, + top: 290.h, + child: Opacity( + opacity: 0.50.w, + child: Text( + '26', textAlign: TextAlign.center, style: TextStyle( color: Colors.white /* white */, - fontSize: 50.w, + fontSize: 55.w, fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w700, - letterSpacing: -0.50.h, + fontWeight: FontWeight.w600, + letterSpacing: -0.55.w, ), ), - ], - ), - ), - ), - Positioned( - left: 85.w, - top: 290.h, - child: Opacity( - opacity: 0.50.w, - child: Text( - '26', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white /* white */, - fontSize: 55.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - letterSpacing: -0.55.w, ), ), - ), - ), - Positioned( - left: 104.w, - top: 1210.h, - child: Text( - '1', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color(0xFFFF9F1C) /* main-orange */, - fontSize: 55.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - letterSpacing: -0.55.h, + Positioned( + left: 104.w, + top: 1210.h, + child: Text( + '1', + textAlign: TextAlign.center, + style: TextStyle( + color: const Color(0xFFFF9F1C) /* main-orange */, + fontSize: 55.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + letterSpacing: -0.55.h, + ), + ), ), - ), - ), - Positioned( - left: 85.w, - top: 562.h, - child: Text( - '27', - textAlign: TextAlign.center, - style: TextStyle( - color: const Color(0xFFFF9F1C) /* main-orange */, - fontSize: 55.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - letterSpacing: -0.55.w, + Positioned( + left: 85.w, + top: 562.h, + child: Text( + '27', + textAlign: TextAlign.center, + style: TextStyle( + color: const Color(0xFFFF9F1C) /* main-orange */, + fontSize: 55.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + letterSpacing: -0.55.w, + ), + ), ), - ), - ), - Positioned( - left: 100.w, - top: 1482.h, - child: Text( - '2', - textAlign: TextAlign.center, - style: TextStyle( - color: Colors.white /* white */, - fontSize: 55.w, - fontFamily: 'Wanted Sans', - fontWeight: FontWeight.w600, - letterSpacing: -0.55.h, + Positioned( + left: 100.w, + top: 1482.h, + child: Text( + '2', + textAlign: TextAlign.center, + style: TextStyle( + color: Colors.white /* white */, + fontSize: 55.w, + fontFamily: 'Wanted Sans', + fontWeight: FontWeight.w600, + letterSpacing: -0.55.h, + ), + ), ), - ), + ], ), ], ), diff --git a/lib/features/study/ui/make_group/study_make_group_name_screen.dart b/lib/features/study/ui/make_group/study_make_group_name_screen.dart index 179fc91..c8bacfa 100644 --- a/lib/features/study/ui/make_group/study_make_group_name_screen.dart +++ b/lib/features/study/ui/make_group/study_make_group_name_screen.dart @@ -75,15 +75,7 @@ class _StudyMakeGroupName extends State { ), ); - user.joinedGroups = [ - ...user.joinedGroups, - GroupModel( - authorId: user.email, - name: groupName, - tags: tags, - imageUrl: imagePath, - ), - ]; + try { var response = await request.send(); // 🚀 요청 전송 diff --git a/lib/features/study/ui/study_screen.dart b/lib/features/study/ui/study_screen.dart index 525468d..baa41d4 100644 --- a/lib/features/study/ui/study_screen.dart +++ b/lib/features/study/ui/study_screen.dart @@ -1,7 +1,6 @@ import 'package:componentss/core/user_provider.dart'; import 'package:componentss/features/study/data/group_api.dart'; import 'package:componentss/features/study/data/group_model.dart'; -import 'package:componentss/features/study/data/tempGroup.dart'; import 'package:componentss/features/study/ui/group_detail/group_detaill.dart'; import 'package:componentss/features/study/ui/search_group/search_group_screen.dart'; import 'package:componentss/features/study/ui/make_group/study_make_group_screen.dart'; diff --git a/lib/features/study/ui/widgets/interview_schedule_card.dart b/lib/features/study/ui/widgets/interview_schedule_card.dart index 5673e5f..988c598 100644 --- a/lib/features/study/ui/widgets/interview_schedule_card.dart +++ b/lib/features/study/ui/widgets/interview_schedule_card.dart @@ -1,5 +1,4 @@ import 'package:componentss/features/study/data/group_model.dart'; -import 'package:componentss/features/study/data/tempGroup.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; diff --git a/lib/features/study/ui/widgets/ranking_card.dart b/lib/features/study/ui/widgets/ranking_card.dart index 758c5f5..f16276e 100644 --- a/lib/features/study/ui/widgets/ranking_card.dart +++ b/lib/features/study/ui/widgets/ranking_card.dart @@ -1,5 +1,4 @@ import 'package:componentss/features/study/data/group_model.dart'; -import 'package:componentss/features/study/data/tempGroup.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter/material.dart'; diff --git a/lib/main.dart b/lib/main.dart index ecdc358..5a07245 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,11 +1,8 @@ import 'package:componentss/core/user_provider.dart'; import 'package:componentss/features/auth/onboarding_screen.dart'; -import 'package:componentss/features/baking/questions/odd/odd_quiz.dart'; -import 'package:componentss/features/baking/questions/odd/odd_screen.dart'; -import 'package:componentss/features/baking/questions/trend/trend_quiz.dart'; +import 'package:componentss/features/baking/UI/questions/odd/odd_quiz.dart'; +import 'package:componentss/features/baking/UI/questions/odd/odd_screen.dart'; import 'package:componentss/features/main_screen.dart'; -import 'package:componentss/features/baking/questions/trend/trend_ox.dart'; -import 'package:componentss/features/baking/questions/trend/trend_screen.dart'; import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:provider/provider.dart';