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 합니다. // HomePage에서 사용하던 CaseStudyPlan 모델을 PlanPage에서도 사용하거나, // PlanPage에 필요한 별도의 데이터 모델을 정의할 수 있습니다. // 여기서는 동일한 모델을 재사용한다고 가정합니다. class CaseStudyPlan { final String planId; final String planTitle; final String planTeacher; final String thumbnail; CaseStudyPlan({ required this.planId, required this.planTitle, required this.planTeacher, required this.thumbnail, }); factory CaseStudyPlan.fromJson(Map json) { return CaseStudyPlan( planId: json['casestudy lesson id'] ?? '아이디 없음', planTitle: json['course_name'] ?? '제목 없음', planTeacher: json['planTeacher'] ?? '', thumbnail: json['course_thumbnail'] ?? '', ); } } 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://helloworld2-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: () { 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, }, ), ), ); }, 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: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Text( plan.planTitle, style: const TextStyle( fontSize: 17.0, fontWeight: FontWeight.bold), overflow: TextOverflow.ellipsis, maxLines: 2, ), ), const SizedBox(width: 8.0), Text( plan.planTeacher, style: const TextStyle( fontSize: 13.0, color: Colors.grey), maxLines: 1, overflow: TextOverflow.ellipsis, ), ], ), const SizedBox(height: 8.0), if (plan.thumbnail.isNotEmpty) ClipRRect( borderRadius: BorderRadius.circular(8.0), child: Image.network( plan.thumbnail, height: 120, 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)))), ], ), ), ), ); }, ); } }, ); } }