import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; import 'plan_page_detail.dart'; // <<< plan_page_detail.dart 파일을 import 합니다. import 'common/data/case_study_plan.dart'; class PlanPage extends StatefulWidget { const PlanPage({super.key}); @override State createState() => _PlanPageState(); } class _PlanPageState extends State { late Future> _planPageData; @override void initState() { super.initState(); // PlanPage가 처음 생성되거나 화면에 표시될 때 데이터를 가져옵니다. _planPageData = _fetchPlanData(); } Future> _fetchPlanData() async { // HomePage와 동일한 API 주소를 사용합니다. final response = await http .get(Uri.parse('https://helloworld6-ad2uqhckxq-uc.a.run.app')); if (response.statusCode == 200) { final Map decodedJson = json.decode(response.body); if (decodedJson.containsKey('data') && decodedJson['data'] is List) { final List plansJson = decodedJson['data']; return plansJson .map((jsonItem) => CaseStudyPlan.fromJson(jsonItem as Map)) .toList(); } else { throw Exception( 'Invalid data format for PlanPage: "data" field is missing or not a list.'); } } else { throw Exception( 'Failed to load data for PlanPage. Status Code: ${response.statusCode}'); } } @override Widget build(BuildContext context) { return FutureBuilder>( future: _planPageData, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { return Center( child: Padding( padding: const EdgeInsets.all(16.0), child: Text('Error loading PlanPage data: ${snapshot.error}', textAlign: TextAlign.center, maxLines: 3, overflow: TextOverflow.ellipsis, ))); } else if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text('No data available for PlanPage.')); } else { // 데이터 로딩 성공 시 ListView 표시 final plans = snapshot.data!; return ListView.builder( itemCount: plans.length, itemBuilder: (context, index) { final plan = plans[index]; return InkWell( // onTap: () { // }, child: Card( margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), elevation: 2.0, child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 8.0), if (plan.thumbnail.isNotEmpty) ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.network( plan.thumbnail, height: 200, width: double.infinity, fit: BoxFit.contain, alignment: Alignment.centerLeft, loadingBuilder: (BuildContext context, Widget child, ImageChunkEvent? loadingProgress) { if (loadingProgress == null) return child; return Center( child: CircularProgressIndicator( value: loadingProgress.expectedTotalBytes != null ? loadingProgress.cumulativeBytesLoaded / loadingProgress.expectedTotalBytes! : null, ), ); }, errorBuilder: (BuildContext context, Object exception, StackTrace? stackTrace) { return Container( height: 140, color: Colors.grey[200], child: const Center( child: Icon(Icons.broken_image, color: Colors.grey, size: 40))); }, ), ) else Container( height: 140, color: Colors.grey[200], child: const Center( child: Text('No Image', style: TextStyle(color: Colors.grey)))), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( plan.planTitle, style: const TextStyle( fontSize: 20.0, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, maxLines: 2, ), const SizedBox(height: 5.0), // Text( // plan.planTeacher, // style: const TextStyle( // fontSize: 15.0, color: Colors.black45), // maxLines: 1, // overflow: TextOverflow.ellipsis, // ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( "Progress", style: const TextStyle( fontSize: 15.0, color: Colors.black45), overflow: TextOverflow.ellipsis, maxLines: 1, ), ), const SizedBox(width: 5.0), Text( "50%", style: const TextStyle( fontSize: 15.0, color: Colors.black45), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), const SizedBox(height: 8.0), LinearProgressIndicator( value: 0.5, // TODO: Replace with actual progress value from plan object backgroundColor: Colors.grey[200], valueColor: AlwaysStoppedAnimation(Colors.blue), ), const SizedBox(height: 16.0), Column( mainAxisAlignment: MainAxisAlignment.center, // 세로 중앙 정렬 crossAxisAlignment: CrossAxisAlignment.stretch, // 버튼이 가로로 꽉 차게 설정 children: [ElevatedButton( onPressed: () { // TODO: Implement navigation or action for Continue Learning if (plan.planId == 'ID 없음' || plan.planId.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('유효한 Plan ID가 없어 상세 페이지로 이동할 수 없습니다.')), ); return; } Navigator.push( context, MaterialPageRoute( builder: (context) => const PlanPageDetail(), settings: RouteSettings( // <<< Map 형태로 planId와 planTitle을 전달 >>> arguments: { 'planId': plan.planId, 'planTitle': plan.planTitle, }, ), ), ); }, style: ElevatedButton.styleFrom( backgroundColor: const Color(0xB91459DB), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), padding: const EdgeInsets.symmetric(horizontal: 20), ), child: const Text('Continue Learning',style: TextStyle(color: Colors.white)), ),],), ], ), ], ), ), ), ); }, ); } }, ); } }