일단 잡까지 되는것 저장
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:csp2/common/widgets/custom_bottom_nav_bar.dart';
|
import 'package:csp2/common/widgets/job_card.dart';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'package:csp2/job.dart';
|
||||||
class JobsPage extends StatefulWidget {
|
class JobsPage extends StatefulWidget {
|
||||||
const JobsPage({super.key});
|
const JobsPage({super.key});
|
||||||
|
|
||||||
@@ -10,41 +12,63 @@ class JobsPage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _JobsPageState extends State<JobsPage> {
|
class _JobsPageState extends State<JobsPage> {
|
||||||
int _selectedIndex = 3; // Assuming Jobs is the 4th item (index 3)
|
|
||||||
String? _selectedDropdownValue; // 드롭다운 선택 값 저장 변수
|
String? _selectedDropdownValue; // 드롭다운 선택 값 저장 변수
|
||||||
|
String _selectedJobTag = 'Hair'; // 초기 탭 선택 값
|
||||||
|
List<Job>? _jobData; // Job 모델 객체 리스트로 변경
|
||||||
|
bool _isLoading = true;
|
||||||
|
|
||||||
|
final List<String> _jobTags = ['Hair', 'Skincare', 'Bodycare', 'Service', 'IT', 'Education'];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_selectedDropdownValue = 'Indonesia'; // 초기값 설정
|
_selectedDropdownValue = 'Indonesia'; // 초기값 설정
|
||||||
|
_fetchJobData();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onItemTapped(int index) {
|
Future<void> _fetchJobData() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedIndex = index;
|
_isLoading = true;
|
||||||
});
|
});
|
||||||
// TODO: Add navigation logic here based on index
|
try {
|
||||||
// For now, we'll just print the index
|
final response = await http.get(Uri.parse(
|
||||||
print('Tapped index: $index');
|
'https://helloworld4-ad2uqhckxq-uc.a.run.app/?country=$_selectedDropdownValue&jobtag=$_selectedJobTag'));
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
setState(() {
|
||||||
|
_jobData = (json.decode(response.body) as List)
|
||||||
|
.map((item) => Job.fromJson(item))
|
||||||
|
.toList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
_jobData = null; // Clear data on error
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
_jobData = null; // Clear data on error
|
||||||
|
} finally {
|
||||||
|
setState(() {
|
||||||
|
_isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return DefaultTabController(
|
||||||
body: Padding(
|
length: _jobTags.length,
|
||||||
padding: const EdgeInsets.all(16.0), // 상단에 패딩 추가
|
child: Scaffold(
|
||||||
child: Column(
|
body: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start, // 상단 정렬
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start, // 왼쪽 정렬
|
|
||||||
children: [
|
children: [
|
||||||
DropdownButton<String>(
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
||||||
|
child: DropdownButton<String>(
|
||||||
value: _selectedDropdownValue,
|
value: _selectedDropdownValue,
|
||||||
onChanged: (String? newValue) {
|
onChanged: (String? newValue) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_selectedDropdownValue = newValue;
|
_selectedDropdownValue = newValue;
|
||||||
});
|
});
|
||||||
|
_fetchJobData(); // Fetch data when dropdown changes
|
||||||
},
|
},
|
||||||
items: <String>['Indonesia', 'Hongkong', 'Singapore', 'South Korea',' Japan']
|
items: <String>['Indonesia', 'Hongkong', 'Singapore', 'South Korea', 'Japan']
|
||||||
.map<DropdownMenuItem<String>>((String value) {
|
.map<DropdownMenuItem<String>>((String value) {
|
||||||
return DropdownMenuItem<String>(
|
return DropdownMenuItem<String>(
|
||||||
value: value,
|
value: value,
|
||||||
@@ -52,8 +76,34 @@ class _JobsPageState extends State<JobsPage> {
|
|||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 20),
|
),
|
||||||
Text('Selected: $_selectedDropdownValue'),
|
TabBar(
|
||||||
|
isScrollable: true,
|
||||||
|
tabs: _jobTags.map((tag) => Tab(text: tag)).toList(),
|
||||||
|
onTap: (index) {
|
||||||
|
setState(() {
|
||||||
|
_selectedJobTag = _jobTags[index];
|
||||||
|
});
|
||||||
|
_fetchJobData();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
children: _jobTags.map((tag) {
|
||||||
|
return _isLoading
|
||||||
|
? const Center(child: CircularProgressIndicator())
|
||||||
|
: _jobData == null || _jobData!.isEmpty
|
||||||
|
? Center(child: Text('No jobs found for $tag.'))
|
||||||
|
: ListView.builder(
|
||||||
|
itemCount: _jobData!.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final job = _jobData![index];
|
||||||
|
return JobCard(job: job);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
122
lib/common/widgets/job_card.dart
Normal file
122
lib/common/widgets/job_card.dart
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'package:csp2/job.dart';
|
||||||
|
class JobCard extends StatelessWidget {
|
||||||
|
final Job job;
|
||||||
|
|
||||||
|
const JobCard({super.key, required this.job});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||||
|
elevation: 3,
|
||||||
|
child: SizedBox(
|
||||||
|
height: 200,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
// 왼쪽 영역
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
job.jobName,
|
||||||
|
style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
job.jobDescription,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
maxLines: 4,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 6),
|
||||||
|
Text(
|
||||||
|
"Show More", // You might want to make this clickable to show full description
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.blue,
|
||||||
|
decoration: TextDecoration.underline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
job.jobJobtag,
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold, color: Colors.black54),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// 오른쪽 영역
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: Container(
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Color(0xFFEFF1FB),
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topRight: Radius.circular(16),
|
||||||
|
bottomRight: Radius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
"\$ ${job.jobIncome}",
|
||||||
|
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
|
||||||
|
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
job.jobIncomeType,
|
||||||
|
style: TextStyle(color: Colors.grey),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
job.jobLocationCity,
|
||||||
|
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
job.jobLocationCountry,
|
||||||
|
style: TextStyle(color: Colors.grey),
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: Color(0xFF3056D3),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(50),
|
||||||
|
),
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||||
|
),
|
||||||
|
child: const Text("지원"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -72,8 +72,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
String _currentTimeZone = 'Loading timezone...';
|
String _currentTimeZone = 'Loading timezone...';
|
||||||
late Stream<DateTime> _clockStream;
|
late Stream<DateTime> _clockStream;
|
||||||
|
|
||||||
int _selectedIndex = 0;
|
|
||||||
|
|
||||||
// --- 추천 클래스 관련 상태 변수 ---
|
// --- 추천 클래스 관련 상태 변수 ---
|
||||||
List<RecommendPlan> _recommendPlans = [];
|
List<RecommendPlan> _recommendPlans = [];
|
||||||
int _currentRecommendIndex = 0;
|
int _currentRecommendIndex = 0;
|
||||||
@@ -146,7 +144,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
_currentTimeZone = 'Failed to get timezone.';
|
_currentTimeZone = 'Failed to get timezone.';
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
print('Could not get timezone: $e');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,7 +183,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
'Failed to load recommend plans. Status Code: ${response.statusCode}');
|
'Failed to load recommend plans. Status Code: ${response.statusCode}');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
print('Error fetching recommend plans: $e');
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isLoadingRecommends = false;
|
_isLoadingRecommends = false;
|
||||||
@@ -226,7 +222,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
return Container(
|
return Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 12.0),
|
||||||
color: Theme.of(context).primaryColor.withOpacity(0.1),
|
color: Theme.of(context).primaryColor.withAlpha(25),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@@ -242,123 +238,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 추천 클래스 섹션 위젯 ---
|
|
||||||
Widget _buildRecommendSection() {
|
|
||||||
if (_isLoadingRecommends) {
|
|
||||||
return const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 20.0),
|
|
||||||
child: Center(child: CircularProgressIndicator()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_hasRecommendError) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 16.0),
|
|
||||||
child: Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(Icons.error_outline, color: Colors.red, size: 40),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'Failed to load recommendations.\n$_recommendErrorText',
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: const TextStyle(color: Colors.redAccent),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
ElevatedButton.icon(
|
|
||||||
icon: const Icon(Icons.refresh),
|
|
||||||
label: const Text('Retry'),
|
|
||||||
onPressed: _fetchRecommendPlans,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_recommendPlans.isEmpty) {
|
|
||||||
return const Padding(
|
|
||||||
padding: EdgeInsets.symmetric(vertical: 20.0),
|
|
||||||
child: Center(child: Text('No recommendations available at the moment.')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final currentPlan = _recommendPlans[_currentRecommendIndex];
|
|
||||||
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 16.0), // 하단 마진 추가
|
|
||||||
padding: const EdgeInsets.all(12.0),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).cardColor, // 카드 색상 사용
|
|
||||||
borderRadius: BorderRadius.circular(12.0),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: Colors.grey.withOpacity(0.2),
|
|
||||||
spreadRadius: 1,
|
|
||||||
blurRadius: 4,
|
|
||||||
offset: const Offset(0, 2),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
'✨ Recommend Classes', // 이모지 추가
|
|
||||||
style: Theme.of(context).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
|
|
||||||
),
|
|
||||||
|
|
||||||
const SizedBox(height: 12.0),
|
|
||||||
|
|
||||||
Text(
|
|
||||||
currentPlan.planName,
|
|
||||||
style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
|
||||||
maxLines: 2,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
),
|
|
||||||
// Test: Plan ID 표시 (디버깅용)
|
|
||||||
// Text('ID: ${currentPlan.planId}', style: TextStyle(fontSize: 10, color: Colors.grey)),
|
|
||||||
|
|
||||||
AspectRatio(
|
|
||||||
aspectRatio: 16 / 9,
|
|
||||||
child: ClipRRect(
|
|
||||||
borderRadius: BorderRadius.circular(8.0),
|
|
||||||
child: currentPlan.thumbnail.isNotEmpty
|
|
||||||
? Image.network(
|
|
||||||
currentPlan.thumbnail,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) {
|
|
||||||
if (loadingProgress == null) return child;
|
|
||||||
return Container( // 로딩 중 배경색 및 인디케이터 중앙 정렬 개선
|
|
||||||
color: Colors.grey[200],
|
|
||||||
child: Center(
|
|
||||||
child: CircularProgressIndicator(
|
|
||||||
value: loadingProgress.expectedTotalBytes != null
|
|
||||||
? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes!
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) {
|
|
||||||
return Container(
|
|
||||||
color: Colors.grey[200],
|
|
||||||
child: const Center(child: Icon(Icons.broken_image, color: Colors.grey, size: 50)),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Container(
|
|
||||||
color: Colors.grey[200],
|
|
||||||
child: const Center(child: Text('No Image Available', style: TextStyle(color: Colors.grey))),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// --- 추천 클래스 섹션 위젯 끝 ---
|
|
||||||
|
|
||||||
Widget _buildHomeContent() {
|
Widget _buildHomeContent() {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
@@ -515,18 +394,6 @@ class _HomePageState extends State<HomePage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onItemTapped(int index) {
|
|
||||||
setState(() {
|
|
||||||
_selectedIndex = index;
|
|
||||||
// 홈 탭(인덱스 0)으로 돌아올 때 추천 타이머를 다시 시작할 수 있도록 고려
|
|
||||||
if (index == 0 && _recommendPlans.isNotEmpty && (_recommendTimer == null || !_recommendTimer!.isActive)) {
|
|
||||||
_startRecommendTimer();
|
|
||||||
} else if (index != 0) {
|
|
||||||
_recommendTimer?.cancel(); // 다른 탭으로 이동 시 타이머 일시 중지
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> pageContents = _buildPageContents();
|
final List<Widget> pageContents = _buildPageContents();
|
||||||
@@ -536,7 +403,7 @@ class _HomePageState extends State<HomePage> {
|
|||||||
// title: Text(_selectedIndex == 0 ? 'Home' : 'Plan Page'),
|
// title: Text(_selectedIndex == 0 ? 'Home' : 'Plan Page'),
|
||||||
// ),
|
// ),
|
||||||
body: IndexedStack( // AppBar가 없으므로 SafeArea로 감싸는 것을 고려해볼 수 있습니다.
|
body: IndexedStack( // AppBar가 없으므로 SafeArea로 감싸는 것을 고려해볼 수 있습니다.
|
||||||
index: _selectedIndex,
|
index: 0,
|
||||||
children: pageContents,
|
children: pageContents,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
41
lib/job.dart
Normal file
41
lib/job.dart
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
class Job {
|
||||||
|
final String jobName;
|
||||||
|
final String jobJobtag;
|
||||||
|
final String jobDescription;
|
||||||
|
final double jobIncome;
|
||||||
|
final String jobIncomeType;
|
||||||
|
final String jobLocationCountry;
|
||||||
|
final String jobLocationCity;
|
||||||
|
final String jobRequirement;
|
||||||
|
final String jobTag;
|
||||||
|
final String jobEndpoint;
|
||||||
|
|
||||||
|
Job({
|
||||||
|
required this.jobName,
|
||||||
|
required this.jobJobtag,
|
||||||
|
required this.jobDescription,
|
||||||
|
required this.jobIncome,
|
||||||
|
required this.jobIncomeType,
|
||||||
|
required this.jobLocationCountry,
|
||||||
|
required this.jobLocationCity,
|
||||||
|
required this.jobRequirement,
|
||||||
|
required this.jobTag,
|
||||||
|
required this.jobEndpoint,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory Job.fromJson(Map<String, dynamic> json) {
|
||||||
|
return Job(
|
||||||
|
jobName: json['Job_name'] as String,
|
||||||
|
jobJobtag: json['job_jobtag'] as String,
|
||||||
|
jobDescription: json['job_decriptopn'] as String,
|
||||||
|
jobIncome: double.tryParse(json['job_Income'].toString()) ?? 0.0,
|
||||||
|
jobIncomeType: json['job_income_type'] as String,
|
||||||
|
jobLocationCountry: json['job_location_country'] as String,
|
||||||
|
jobLocationCity: json['job_location_city'] as String,
|
||||||
|
jobRequirement: json['job_requirment'] as String,
|
||||||
|
jobTag: json['job_tag'] as String,
|
||||||
|
jobEndpoint: json['job_endpoint'] as String,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -64,7 +64,6 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_selectedIndex = index;
|
_selectedIndex = index;
|
||||||
});
|
});
|
||||||
print('Selected index: $_selectedIndex'); // 디버깅 출력 추가
|
|
||||||
// HomePage의 _recommendTimer 제어 로직은 HomePage 내부에서 독립적으로 관리됩니다.
|
// HomePage의 _recommendTimer 제어 로직은 HomePage 내부에서 독립적으로 관리됩니다.
|
||||||
// 또는 필요에 따라 GlobalKey 등을 사용하여 HomePage의 상태에 접근할 수 있습니다.
|
// 또는 필요에 따라 GlobalKey 등을 사용하여 HomePage의 상태에 접근할 수 있습니다.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,12 @@ class _PlanPageDetailState extends State<PlanPageDetail> {
|
|||||||
width: 110,
|
width: 110,
|
||||||
margin: const EdgeInsets.symmetric(
|
margin: const EdgeInsets.symmetric(
|
||||||
horizontal: 8.0, vertical: 8.0),
|
horizontal: 8.0, vertical: 8.0),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: _selectedItem == item
|
||||||
|
? Border.all(color: Colors.blueAccent, width: 3)
|
||||||
|
: null,
|
||||||
|
borderRadius: BorderRadius.circular(8.0),
|
||||||
|
),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
@@ -239,8 +245,9 @@ class _PlanPageDetailState extends State<PlanPageDetail> {
|
|||||||
Widget child,
|
Widget child,
|
||||||
ImageChunkEvent?
|
ImageChunkEvent?
|
||||||
loadingProgress) {
|
loadingProgress) {
|
||||||
if (loadingProgress == null)
|
if (loadingProgress == null) {
|
||||||
return child;
|
return child;
|
||||||
|
}
|
||||||
return Center(
|
return Center(
|
||||||
child:
|
child:
|
||||||
CircularProgressIndicator(
|
CircularProgressIndicator(
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ class _YoutubePlayerPageState extends State<YoutubePlayerPage> {
|
|||||||
String _videoTitle = 'YouTube Video';
|
String _videoTitle = 'YouTube Video';
|
||||||
final int _currentBottomNavIndex = 0;
|
final int _currentBottomNavIndex = 0;
|
||||||
bool _isSystemUiVisible = true;
|
bool _isSystemUiVisible = true;
|
||||||
bool _isLoading = true; // 로딩 상태 추가
|
|
||||||
bool _isFullScreen = false;
|
bool _isFullScreen = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -124,18 +123,6 @@ class _YoutubePlayerPageState extends State<YoutubePlayerPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onNotificationTapped() {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(content: Text('알림 아이콘 클릭됨')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onProfileTapped() {
|
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
|
||||||
const SnackBar(content: Text('프로필 아이콘 클릭됨')),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onBottomNavItemTapped(int index) {
|
void _onBottomNavItemTapped(int index) {
|
||||||
if (_currentBottomNavIndex == index && index != 0) return;
|
if (_currentBottomNavIndex == index && index != 0) return;
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
@@ -195,9 +182,18 @@ class _YoutubePlayerPageState extends State<YoutubePlayerPage> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final bool isFullScreen = _controller?.value.isFullScreen ?? false;
|
final bool isFullScreen = _controller?.value.isFullScreen ?? false;
|
||||||
|
|
||||||
// <<< WillPopScope로 Scaffold를 감싸서 뒤로가기 이벤트 가로채기 >>>
|
// <<< PopScope로 Scaffold를 감싸서 뒤로가기 이벤트 가로채기 >>>
|
||||||
return WillPopScope(
|
return PopScope(
|
||||||
onWillPop: _onWillPop,
|
canPop: false,
|
||||||
|
onPopInvoked: (didPop) async {
|
||||||
|
if (didPop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final bool shouldPop = await _onWillPop();
|
||||||
|
if (shouldPop) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
},
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
extendBodyBehindAppBar: isFullScreen,
|
extendBodyBehindAppBar: isFullScreen,
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user