diff --git a/lib/career_page.dart b/lib/career_page.dart new file mode 100644 index 0000000..39917bf --- /dev/null +++ b/lib/career_page.dart @@ -0,0 +1,62 @@ + +import 'package:flutter/material.dart'; +import 'package:csp2/common/widgets/custom_bottom_nav_bar.dart'; + +class JobsPage extends StatefulWidget { + const JobsPage({super.key}); + + @override + State createState() => _JobsPageState(); +} + +class _JobsPageState extends State { + int _selectedIndex = 3; // Assuming Jobs is the 4th item (index 3) + String? _selectedDropdownValue; // 드롭다운 선택 값 저장 변수 + + @override + void initState() { + super.initState(); + _selectedDropdownValue = 'Indonesia'; // 초기값 설정 + } + + void _onItemTapped(int index) { + setState(() { + _selectedIndex = index; + }); + // TODO: Add navigation logic here based on index + // For now, we'll just print the index + print('Tapped index: $index'); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Padding( + padding: const EdgeInsets.all(16.0), // 상단에 패딩 추가 + child: Column( + mainAxisAlignment: MainAxisAlignment.start, // 상단 정렬 + crossAxisAlignment: CrossAxisAlignment.start, // 왼쪽 정렬 + children: [ + DropdownButton( + value: _selectedDropdownValue, + onChanged: (String? newValue) { + setState(() { + _selectedDropdownValue = newValue; + }); + }, + items: ['Indonesia', 'Hongkong', 'Singapore', 'South Korea',' Japan'] + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ), + const SizedBox(height: 20), + Text('Selected: $_selectedDropdownValue'), + ], + ), + ), + ); + } +} diff --git a/lib/common/widgets/custom_bottom_nav_bar.dart b/lib/common/widgets/custom_bottom_nav_bar.dart index af7909e..9213082 100644 --- a/lib/common/widgets/custom_bottom_nav_bar.dart +++ b/lib/common/widgets/custom_bottom_nav_bar.dart @@ -35,7 +35,7 @@ class CustomBottomNavBar extends StatelessWidget { ), BottomNavigationBarItem( icon: Icon(Icons.work_outline), - label: 'Jobs', + label: 'Career', ), BottomNavigationBarItem( icon: Icon(Icons.more_horiz_outlined), diff --git a/lib/home_page.dart b/lib/home_page.dart index 910d67a..919a271 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -22,10 +22,10 @@ class CaseStudyPlan { factory CaseStudyPlan.fromJson(Map json) { return CaseStudyPlan( - planId: json['planId'] ?? '아이디 없음', - planTitle: json['planTitle'] ?? '제목 없음', - planTeacher: json['planTeacher'] ?? '선생님 정보 없음', - thumbnail: json['thumbnail'] ?? '', + planId: json['casestudy lesson id'] ?? '아이디 없음', + planTitle: json['course_name'] ?? '제목 없음', + planTeacher: json['planTeacher'] ?? '', + thumbnail: json['course_thumbnail'] ?? '', ); } } @@ -460,7 +460,8 @@ class _HomePageState extends State { ), const SizedBox(width: 8.0), Text( - plan.planTeacher, + // plan.planTeacher, + "", style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey[600]), ), ], @@ -508,7 +509,7 @@ class _HomePageState extends State { ), // --- ▼▼▼ 추천 클래스 섹션 호출 ▼▼▼ --- - _buildRecommendSection(), + // _buildRecommendSection(), // --- ▲▲▲ 추천 클래스 섹션 호출 끝 ▲▲▲ --- ], ); diff --git a/lib/jobs_page.dart b/lib/jobs_page.dart deleted file mode 100644 index 65837e1..0000000 --- a/lib/jobs_page.dart +++ /dev/null @@ -1,32 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:csp2/common/widgets/custom_bottom_nav_bar.dart'; - -class JobsPage extends StatefulWidget { - const JobsPage({super.key}); - - @override - State createState() => _JobsPageState(); -} - -class _JobsPageState extends State { - int _selectedIndex = 3; // Assuming Jobs is the 4th item (index 3) - - void _onItemTapped(int index) { - setState(() { - _selectedIndex = index; - }); - // TODO: Add navigation logic here based on index - // For now, we'll just print the index - print('Tapped index: $index'); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - body: const Center( - child: Text('Jobs Page'), - ), - ); - } -} diff --git a/lib/main.dart b/lib/main.dart index 3be5741..3eeaf46 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,7 +6,7 @@ import 'package:flutter/material.dart'; import 'home_page.dart'; // HomePage에 콜백을 전달해야 하므로 import 경로 확인 import 'plan_page.dart'; import 'statistics_page.dart'; -import 'jobs_page.dart'; +import 'career_page.dart'; import 'more_page.dart'; import 'common/widgets/custom_bottom_nav_bar.dart'; @@ -31,14 +31,15 @@ class MyApp extends StatelessWidget { } class MyHomePage extends StatefulWidget { - const MyHomePage({super.key}); + final int initialIndex; + const MyHomePage({super.key, this.initialIndex = 0}); @override State createState() => _MyHomePageState(); } class _MyHomePageState extends State { - int _selectedIndex = 0; + late int _selectedIndex; // 각 탭에 연결될 페이지 위젯 리스트 // HomePage는 StatefulWidget이므로 const를 붙이지 않습니다. @@ -48,6 +49,7 @@ class _MyHomePageState extends State { @override void initState() { super.initState(); + _selectedIndex = widget.initialIndex; // *** 수정: HomePage 생성 시 onNavigateToPlanTab 콜백 전달 *** _widgetOptions = [ HomePage(onNavigateToPlanTab: _onItemTapped), // 콜백 함수 전달 @@ -87,7 +89,7 @@ class _MyHomePageState extends State { appBarTitle = 'Statistics'; } else if (_selectedIndex == 3) { - appBarTitle = 'Jobs'; + appBarTitle = 'Career'; } else if (_selectedIndex == 4) { appBarTitle = 'More'; diff --git a/lib/plan_page.dart b/lib/plan_page.dart index 3414cd8..d7168e1 100644 --- a/lib/plan_page.dart +++ b/lib/plan_page.dart @@ -21,10 +21,10 @@ class CaseStudyPlan { factory CaseStudyPlan.fromJson(Map json) { return CaseStudyPlan( - planId: json['planId'] ?? 'ID 없음', - planTitle: json['planTitle'] ?? '제목 없음', - planTeacher: json['planTeacher'] ?? '선생님 정보 없음', - thumbnail: json['thumbnail'] ?? '', + planId: json['casestudy lesson id'] ?? '아이디 없음', + planTitle: json['course_name'] ?? '제목 없음', + planTeacher: json['planTeacher'] ?? '', + thumbnail: json['course_thumbnail'] ?? '', ); } } @@ -147,14 +147,16 @@ class _PlanPageState extends State { ], ), const SizedBox(height: 8.0), + if (plan.thumbnail.isNotEmpty) ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.network( plan.thumbnail, - height: 140, + height: 120, width: double.infinity, - fit: BoxFit.cover, + fit: BoxFit.contain, + alignment: Alignment.centerLeft, loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { if (loadingProgress == null) return child; diff --git a/lib/plan_page_detail.dart b/lib/plan_page_detail.dart index 73b7675..4c236f2 100644 --- a/lib/plan_page_detail.dart +++ b/lib/plan_page_detail.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; +import 'main.dart'; import 'youtube_player_page.dart'; // YoutubePlayerPage import import 'common/widgets/custom_bottom_nav_bar.dart'; @@ -10,12 +11,16 @@ class PlanDetailItem { final String lessonTag; final String lessonUrl; final String thumbnail; + final String lessonName; + final String lessonDescription; PlanDetailItem({ required this.lessonId, required this.lessonTag, required this.lessonUrl, required this.thumbnail, + required this.lessonName, + required this.lessonDescription, }); factory PlanDetailItem.fromJson(Map json) { @@ -24,6 +29,8 @@ class PlanDetailItem { lessonTag: json['lesson tag'] ?? '태그 없음', lessonUrl: json['lesson url'] ?? 'URL 없음', thumbnail: json['thumbnail'] ?? '', + lessonName: json['lesson_name'] ?? '이름 없음', + lessonDescription: json['lesson_description'] ?? '설명 없음', ); } } @@ -43,6 +50,7 @@ class _PlanPageDetailState extends State { Future>? _planDetails; late int _currentBottomNavIndex; String? _selectedYoutubeUrl; + PlanDetailItem? _selectedItem; // <<< 선택된 아이템을 저장할 변수 추가 @override void initState() { @@ -80,7 +88,6 @@ class _PlanPageDetailState extends State { }); _planDetails = Future.error(Exception("전달된 인자가 올바르지 않습니다. (Map 형태여야 함)")); - } } } @@ -111,28 +118,10 @@ class _PlanPageDetailState extends State { setState(() { _currentBottomNavIndex = index; }); - if (index == 0) { - Navigator.of(context).popUntil((route) => route.isFirst); - } else { - String tabName = ''; - switch (index) { - case 1: - tabName = 'Plan'; - break; - case 2: - tabName = 'Statistics'; - break; - case 3: - tabName = 'Jobs'; // New case for Jobs - break; - case 4: - tabName = 'More'; // Updated case for More - break; - } - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('$tabName 탭으로 이동 (구현 필요)')), - ); - } + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute(builder: (context) => MyHomePage(initialIndex: index)), + (Route route) => false, + ); } @override @@ -171,6 +160,9 @@ class _PlanPageDetailState extends State { return const Center(child: Text('세부 계획 데이터가 없습니다.')); } else { final details = snapshot.data!; + if (_selectedItem == null && details.isNotEmpty) { + _selectedItem = details.first; + } // 첫 번째 비디오의 URL을 가져와 _selectedYoutubeUrl을 초기화합니다. if (_selectedYoutubeUrl == null && details.isNotEmpty) { @@ -178,12 +170,12 @@ class _PlanPageDetailState extends State { (item) => item.lessonUrl.isNotEmpty && (item.lessonUrl.contains('youtube.com') || item.lessonUrl.contains('youtu.be')), - orElse: () => PlanDetailItem(lessonId: '', lessonTag: '', lessonUrl: '', thumbnail: ''), + orElse: () => PlanDetailItem(lessonId: '', lessonTag: '', lessonUrl: '', thumbnail: '',lessonName: '', lessonDescription: ''), ).lessonUrl.isNotEmpty ? details.firstWhere( (item) => item.lessonUrl.isNotEmpty && (item.lessonUrl.contains('youtube.com') || item.lessonUrl.contains('youtu.be')), - orElse: () => PlanDetailItem(lessonId: '', lessonTag: '', lessonUrl: '', thumbnail: ''), + orElse: () => PlanDetailItem(lessonId: '', lessonTag: '', lessonUrl: '', thumbnail: '',lessonName: '', lessonDescription: ''), ).lessonUrl : null; } @@ -199,19 +191,20 @@ class _PlanPageDetailState extends State { final item = details[index]; return GestureDetector( onTap: () { - if (item.lessonUrl.isNotEmpty && - (item.lessonUrl.contains('youtube.com') || - item.lessonUrl.contains('youtu.be'))) { - setState(() { + setState(() { + _selectedItem = item; + if (item.lessonUrl.isNotEmpty && + (item.lessonUrl.contains('youtube.com') || + item.lessonUrl.contains('youtu.be'))) { _selectedYoutubeUrl = item.lessonUrl; - }); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - '유효한 YouTube URL이 아닙니다: ${item.lessonUrl}')), - ); - } + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '유효한 YouTube URL이 아닙니다: ${item.lessonUrl}')), + ); + } + }); }, child: Container( width: 110, @@ -289,7 +282,82 @@ class _PlanPageDetailState extends State { ); }, ), - )], + ), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _selectedItem!.thumbnail.isNotEmpty + ? ClipRRect( + borderRadius: BorderRadius.circular(12.0), + child: Image.network( + _selectedItem!.thumbnail, + height: 200, + width: double.infinity, + fit: BoxFit.cover, + errorBuilder: (context, error, stackTrace) { + return Container( + height: 200, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(12.0), + ), + child: const Icon(Icons.broken_image, size: 60, color: Colors.grey), + ); + }, + ), + ) + : Container( + height: 200, + width: double.infinity, + decoration: BoxDecoration( + color: Colors.grey[300], + borderRadius: BorderRadius.circular(12.0), + ), + child: const Icon(Icons.image_not_supported, size: 60, color: Colors.grey), + ), + const SizedBox(height: 12), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + _selectedItem!.lessonName, + style: Theme.of(context).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold), + ), + ), + IconButton( + icon: const Icon(Icons.play_circle_fill, size: 40, color: Colors.red), + onPressed: () { + if (_selectedYoutubeUrl != null && _selectedYoutubeUrl!.isNotEmpty) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => YoutubePlayerPage(lessonUrl: _selectedYoutubeUrl!), + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('재생할 수 있는 영상이 없습니다.')), + ); + } + }, + ), + ], + ), + const SizedBox(height: 12), + Text( + _selectedItem!.lessonDescription, + style: Theme.of(context).textTheme.bodyLarge, + ), + ], + ), + ), + ), + ], ); } }, diff --git a/lib/youtube_player_page.dart b/lib/youtube_player_page.dart index 44c7bcf..4f07764 100644 --- a/lib/youtube_player_page.dart +++ b/lib/youtube_player_page.dart @@ -54,7 +54,7 @@ class _YoutubePlayerPageState extends State { _controller = YoutubePlayerController( initialVideoId: _videoId!, flags: const YoutubePlayerFlags( - autoPlay: false, + autoPlay: true, mute: false, ), )..addListener(_playerListener); @@ -150,7 +150,7 @@ class _YoutubePlayerPageState extends State { tabName = 'Statistics'; break; case 3: - tabName = 'Jobs'; // New case for Jobs + tabName = 'Career'; // New case for Jobs break; case 4: tabName = 'More'; // Updated case for More @@ -247,12 +247,12 @@ class _YoutubePlayerPageState extends State { ), ], ), - // bottomNavigationBar: isFullScreen - // ? null - // : CustomBottomNavBar( - // currentIndex: _currentBottomNavIndex, - // onTap: _onBottomNavItemTapped, - // ), + bottomNavigationBar: isFullScreen + ? null + : CustomBottomNavBar( + currentIndex: _currentBottomNavIndex, + onTap: _onBottomNavItemTapped, + ), ), ); }